mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2025-12-10 02:05:20 +00:00
38
.idea/assetWizardSettings.xml
generated
38
.idea/assetWizardSettings.xml
generated
@@ -106,6 +106,44 @@
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vectorWizard">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="vectorAssetStep">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="clipartAsset">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="url" value="jar:file:/C:/Program%20Files/Android/Android%20Studio/plugins/android/lib/android.jar!/images/material/icons/materialicons/public/baseline_public_24.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="ffffff" />
|
||||
<entry key="outputName" value="ic_baseline_public_24" />
|
||||
<entry key="sourceFile" value="D:\Android Studio Projects\Private work\Altas_-_Weather" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
152
.idea/codeStyles/Project.xml
generated
152
.idea/codeStyles/Project.xml
generated
@@ -1,29 +1,131 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="java.util" alias="false" withSubpackages="false" />
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||
<value>
|
||||
<package name="" alias="false" withSubpackages="true" />
|
||||
<package name="java" alias="false" withSubpackages="true" />
|
||||
<package name="javax" alias="false" withSubpackages="true" />
|
||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||
<package name="" alias="true" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
2
.idea/gradle.xml
generated
2
.idea/gradle.xml
generated
@@ -1,8 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="PLATFORM" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
|
||||
40
.idea/jarRepositories.xml
generated
Normal file
40
.idea/jarRepositories.xml
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="BintrayJCenter" />
|
||||
<option name="name" value="BintrayJCenter" />
|
||||
<option name="url" value="https://jcenter.bintray.com/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="Google" />
|
||||
<option name="name" value="Google" />
|
||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="C:\Users\h_mal\AppData\Local\Android\Sdk\extras\android\m2repository" />
|
||||
<option name="name" value="C:\Users\h_mal\AppData\Local\Android\Sdk\extras\android\m2repository" />
|
||||
<option name="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/extras/android/m2repository/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="C:\Users\h_mal\AppData\Local\Android\Sdk\extras\google\m2repository" />
|
||||
<option name="name" value="C:\Users\h_mal\AppData\Local\Android\Sdk\extras\google\m2repository" />
|
||||
<option name="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/extras/google/m2repository/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="C:\Users\h_mal\AppData\Local\Android\Sdk\extras\m2repository" />
|
||||
<option name="name" value="C:\Users\h_mal\AppData\Local\Android\Sdk\extras\m2repository" />
|
||||
<option name="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/extras/m2repository/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
18
.idea/misc.xml
generated
18
.idea/misc.xml
generated
@@ -5,22 +5,36 @@
|
||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="5">
|
||||
<list size="12">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
|
||||
<item index="6" class="java.lang.String" itemvalue="android.annotation.Nullable" />
|
||||
<item index="7" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
|
||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
|
||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
|
||||
<item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
|
||||
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="4">
|
||||
<list size="11">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
|
||||
<item index="5" class="java.lang.String" itemvalue="android.annotation.NonNull" />
|
||||
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
|
||||
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
|
||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
|
||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
|
||||
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
|
||||
4
.idea/modules.xml
generated
4
.idea/modules.xml
generated
@@ -2,6 +2,10 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Altas_-_Weather.iml" filepath="$PROJECT_DIR$/Altas_-_Weather.iml" group="Altas_-_Weather" />
|
||||
<module fileurl="file://$PROJECT_DIR$/app/Altas_-_Weather-app.iml" filepath="$PROJECT_DIR$/app/Altas_-_Weather-app.iml" group="Altas_-_Weather/app" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Android Studio Projects-Altas_-_Weather.iml" filepath="$PROJECT_DIR$/.idea/modules/Android Studio Projects-Altas_-_Weather.iml" group="Altas_-_Weather" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Android Studio Projects-Altas_-_Weather-app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Android Studio Projects-Altas_-_Weather-app.iml" group="Altas_-_Weather/app" />
|
||||
<module fileurl="file://$PROJECT_DIR$/Weather_app.iml" filepath="$PROJECT_DIR$/Weather_app.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
||||
</modules>
|
||||
|
||||
102
.idea/navEditor.xml
generated
Normal file
102
.idea/navEditor.xml
generated
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="navEditor-manualLayoutAlgorithm2">
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="main_navigation.xml">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="addLocationFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="434" />
|
||||
<option name="y" value="398" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="furtherDetailsFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-20" />
|
||||
<option name="y" value="403" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="nav_home">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="12" />
|
||||
<option name="y" value="12" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_homeFragment_to_furtherDetailsFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="nav_world">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="306" />
|
||||
<option name="y" value="6" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_worldFragment_to_addLocationFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_worldFragment_to_worldItemFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="worldItemFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="213" />
|
||||
<option name="y" value="408" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,15 +1,23 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
// kotlin kapt
|
||||
apply plugin: 'kotlin-kapt'
|
||||
// Android navigation
|
||||
apply plugin: 'androidx.navigation.safeargs'
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
compileSdkVersion 30
|
||||
defaultConfig {
|
||||
applicationId "com.appttude.h_mal.atlas_weather"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 26
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
buildConfigField "String", "ParamOne", "${paramOneEndPoint}"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@@ -17,20 +25,74 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
// To inline the bytecode built with JVM target 1.8 into
|
||||
// bytecode that is being built with JVM target 1.6. (e.g. navArgs)
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'com.android.support:appcompat-v7:26.1.0'
|
||||
implementation 'com.android.support:design:26.1.0'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
implementation 'com.android.support:support-v4:26.1.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||
implementation 'com.android.support:support-vector-drawable:26.1.0'
|
||||
implementation 'androidx.vectordrawable:vectordrawable:1.0.0'
|
||||
implementation "com.google.android.gms:play-services-location:16.0.0"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
|
||||
// Unit testing
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.1'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
|
||||
implementation 'com.android.support:cardview-v7:26.1.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.2.0'
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-test:1.3.71"
|
||||
|
||||
// android unit testing and espresso
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
implementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
//mock websever for testing retrofit responses
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
|
||||
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
||||
|
||||
//mockito and livedata testing
|
||||
testImplementation 'org.mockito:mockito-inline:2.13.0'
|
||||
implementation 'android.arch.core:core-testing'
|
||||
androidTestImplementation 'androidx.test:rules:1.3.0-rc01'
|
||||
|
||||
// Mockk
|
||||
def mockk_ver = "1.10.2"
|
||||
testImplementation "io.mockk:mockk:$mockk_ver"
|
||||
androidTestImplementation "io.mockk:mockk-android:$mockk_ver"
|
||||
|
||||
// Retrofit
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
|
||||
|
||||
// Shared prefs
|
||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||
|
||||
//Kodein Dependency Injection
|
||||
def kodein_version = "6.2.1"
|
||||
implementation "org.kodein.di:kodein-di-generic-jvm:$kodein_version"
|
||||
implementation "org.kodein.di:kodein-di-framework-android-x:$kodein_version"
|
||||
|
||||
// Room database
|
||||
def room_version = "2.3.0-alpha03"
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
// Picasso
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() throws Exception {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.example.h_mal.weather_app", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.appttude.h_mal.atlas_weather.utils
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class DateUtilsKtTest{
|
||||
|
||||
@Test
|
||||
fun toDayString_testStandardData_outputCorrect() {
|
||||
val input = 1606183160
|
||||
|
||||
val result = input.toDayString()
|
||||
|
||||
assertEquals(result, "Nov 24")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toDayName_testStandardData_outputCorrect() {
|
||||
val input = 1606183160
|
||||
|
||||
val result = input.toDayName()
|
||||
|
||||
assertEquals(result, "Tuesday")
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,14 @@
|
||||
android:required="true" />
|
||||
|
||||
<application
|
||||
android:name="com.appttude.h_mal.atlas_weather.application.AppClass"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name="com.appttude.h_mal.atlas_weather.MainActivity"
|
||||
<activity android:name="com.appttude.h_mal.atlas_weather.ui.MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
@@ -35,23 +35,15 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<receiver android:name="com.appttude.h_mal.atlas_weather.NotificationReceiver"
|
||||
android:parentActivityName="com.appttude.h_mal.atlas_weather.MainActivity"/>
|
||||
|
||||
<activity android:name="com.appttude.h_mal.atlas_weather.FurtherInfoActivity"
|
||||
android:parentActivityName="com.appttude.h_mal.atlas_weather.MainActivity"/>
|
||||
<activity
|
||||
android:name="com.appttude.h_mal.atlas_weather.AddForecast"
|
||||
android:parentActivityName="com.appttude.h_mal.atlas_weather.MainActivity" />
|
||||
<activity android:name="com.appttude.h_mal.atlas_weather.UnitSettings"
|
||||
android:name="com.appttude.h_mal.atlas_weather.ui.settings.UnitSettingsActivity"
|
||||
android:label="Settings" />
|
||||
|
||||
<receiver
|
||||
android:name=".notification.NotificationReceiver"
|
||||
android:parentActivityName="com.appttude.h_mal.atlas_weather.MainActivity" />
|
||||
|
||||
<provider
|
||||
android:name="com.appttude.h_mal.atlas_weather.dbfiles.ForecastProvider"
|
||||
android:authorities="com.appttude.h_mal.atlas_weather"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver android:name="com.appttude.h_mal.atlas_weather.AppWidget.NewAppWidget">
|
||||
<receiver android:name=".widget.NewAppWidget">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
|
||||
@@ -63,19 +55,10 @@
|
||||
android:resource="@xml/new_app_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<activity android:name="com.appttude.h_mal.atlas_weather.WorldItemActivity"
|
||||
android:parentActivityName="com.appttude.h_mal.atlas_weather.MainActivity">
|
||||
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name="com.appttude.h_mal.atlas_weather.AppWidget.WidgetRemoteViewsService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS"></service>
|
||||
|
||||
<activity android:name="com.appttude.h_mal.atlas_weather.InfoActivity"
|
||||
android:parentActivityName="com.appttude.h_mal.atlas_weather.MainActivity">
|
||||
|
||||
</activity>
|
||||
android:name=".widget.WidgetRemoteViewsService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,143 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.UriBuilder;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.createUrl;
|
||||
|
||||
public class AddForecast extends AppCompatActivity {
|
||||
|
||||
private EditText tv;
|
||||
private String nameString;
|
||||
public ProgressBar progBarAdd;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_add_forecast);
|
||||
|
||||
tv = findViewById(R.id.location_name_tv);
|
||||
progBarAdd = findViewById(R.id.pb_add);
|
||||
progBarAdd.setVisibility(View.GONE);
|
||||
|
||||
Button button = findViewById(R.id.submit);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
nameString = tv.getText().toString().trim();
|
||||
submitEntry(nameString);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void submitEntry(String s){
|
||||
|
||||
if (TextUtils.isEmpty(s)) {
|
||||
Toast.makeText(AddForecast.this, "please insert a location name", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
new RetrieveFeedTask().execute(s);
|
||||
|
||||
}
|
||||
|
||||
private void insertIntoDb(){
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ForecastEntry.COLUMN_FORECAST_NAME, nameString);
|
||||
|
||||
Uri newUri = getContentResolver().insert(ForecastEntry.CONTENT_URI, values);
|
||||
|
||||
if (newUri == null) {
|
||||
Toast.makeText(this, getString(R.string.insert_entry_failed),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(this, getString(R.string.insert_entry_successful),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class RetrieveFeedTask extends AsyncTask<String, Void, Boolean> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
|
||||
progBarAdd.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
protected Boolean doInBackground(String... urls) {
|
||||
HttpURLConnection urlConnection = null;
|
||||
URL url = createUrl(UriBuilder(urls[0]));
|
||||
|
||||
Boolean b = null;
|
||||
|
||||
try {
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setReadTimeout(30000);
|
||||
urlConnection.setConnectTimeout(30000);
|
||||
urlConnection.setRequestMethod("GET");
|
||||
urlConnection.connect();
|
||||
|
||||
if (urlConnection.getResponseCode() == 200) {
|
||||
b = true;
|
||||
Log.i("", "checkHttpRequest: good response");
|
||||
} else {
|
||||
b = false;
|
||||
Log.e("", "Error response code: " + urlConnection.getResponseCode());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("", "Problem retrieving the response results.", e);
|
||||
} finally {
|
||||
if (urlConnection != null) {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Boolean feed) {
|
||||
|
||||
progBarAdd.setVisibility(View.GONE);
|
||||
|
||||
if (feed == null){
|
||||
Toast.makeText(AddForecast.this, "No connection", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (feed){
|
||||
|
||||
insertIntoDb();
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra("result", getClass().getSimpleName());
|
||||
setResult(RESULT_OK, returnIntent);
|
||||
finish();
|
||||
}else{
|
||||
Toast.makeText(AddForecast.this, "Location not found", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather.AppWidget;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.RemoteViewsService;
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.Forecast;
|
||||
import com.appttude.h_mal.atlas_weather.ForecastItem;
|
||||
import com.appttude.h_mal.atlas_weather.R;
|
||||
import com.appttude.h_mal.atlas_weather.dbfiles.ForecastDBHelper;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.getImageResource;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.extractFeatureFromJson;
|
||||
import static com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry.COLUMN_FORECAST_NAME;
|
||||
import static com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry.COLUMN_WIDGET_FORECAST_ITEM;
|
||||
import static com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry.TABLE_NAME_WIDGET;
|
||||
|
||||
public class MyWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
|
||||
|
||||
private Context mContext;
|
||||
// private ForecastItem forecastItem;
|
||||
private List<Forecast> forecastList;
|
||||
private String TAG = this.getClass().getSimpleName();
|
||||
private Cursor cursor;
|
||||
private SQLiteDatabase database;
|
||||
private int appWidgetId;
|
||||
private SharedPreferences mSettings;
|
||||
|
||||
public MyWidgetRemoteViewsFactory(Context context, Intent intent) {
|
||||
this.mContext = context;
|
||||
this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||
AppWidgetManager.INVALID_APPWIDGET_ID);
|
||||
mSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.i(TAG, "onCreate: widget oncreate executed");
|
||||
ForecastDBHelper forecastsDbhelper = new ForecastDBHelper(mContext);
|
||||
database = forecastsDbhelper.getWritableDatabase();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataSetChanged() {
|
||||
if (cursor != null){
|
||||
cursor.close();
|
||||
}
|
||||
String[] projection = {COLUMN_FORECAST_NAME, COLUMN_WIDGET_FORECAST_ITEM};
|
||||
|
||||
cursor = database.query(TABLE_NAME_WIDGET,
|
||||
projection,
|
||||
COLUMN_FORECAST_NAME + " IS ?",new String[]{"Current"},null,null,null);
|
||||
|
||||
String json;
|
||||
if (cursor != null && cursor.getCount() > 0) {
|
||||
Log.i(TAG, "onDataSetChanged: cursor loaded, count: " + cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
json = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_WIDGET_FORECAST_ITEM));
|
||||
ForecastItem forecastItem = extractFeatureFromJson(json);
|
||||
if (forecastItem != null) {
|
||||
forecastList = forecastItem.getForecastArrayList();
|
||||
forecastList.remove(0);
|
||||
}
|
||||
cursor.close();
|
||||
}else{
|
||||
Log.i(TAG, "onDataSetChanged: cursor is null");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
Log.i(TAG, "getCount: size = " + forecastList.size());
|
||||
return forecastList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteViews getViewAt(int i) {
|
||||
if (i == AdapterView.INVALID_POSITION ||
|
||||
forecastList == null || forecastList.get(i) == null ) {
|
||||
Log.i(this.getClass().getSimpleName(), "getViewAt: no views" );
|
||||
return null;
|
||||
}
|
||||
|
||||
Log.i(this.getClass().getSimpleName(), "getViewAt: views exist " + i);
|
||||
|
||||
RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
|
||||
|
||||
Date updatedate = new Date(forecastList.get(i).getDate_epoch()*1000);
|
||||
SimpleDateFormat format = new SimpleDateFormat("EEE");
|
||||
|
||||
String dateText = format.format(updatedate);
|
||||
// if (dateText.equals("Wednesday")){
|
||||
// dateText = "Wednes..";
|
||||
// }
|
||||
//
|
||||
// Log.i(TAG, "getViewAt: dateText: " + dateText);
|
||||
//
|
||||
// if(i == 0){
|
||||
// SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
|
||||
// String result = sdf.format(Calendar.getInstance().getTime());
|
||||
// rv.setTextViewText(R.id.widget_item_day, result);
|
||||
// }else {
|
||||
rv.setTextViewText(R.id.widget_item_day, dateText);
|
||||
// }
|
||||
|
||||
rv.setImageViewResource(R.id.widget_item_image,
|
||||
getImageResource(forecastList.get(i).getIconURL(),mContext));
|
||||
|
||||
String maxtemp;
|
||||
String mintemp;
|
||||
|
||||
if (mSettings.getString("temp_units","").equals("F°")){
|
||||
maxtemp = String.valueOf(Math.round(forecastList.get(i).getMaxtemp_f())) + "°";
|
||||
mintemp = String.valueOf(Math.round(forecastList.get(i).getMintemp_f())) + "°";
|
||||
}else{
|
||||
maxtemp = String.valueOf(Math.round(forecastList.get(i).getMaxtemp_c())) + "°";
|
||||
mintemp = String.valueOf(Math.round(forecastList.get(i).getMintemp_c())) + "°";
|
||||
}
|
||||
|
||||
rv.setTextViewText(R.id.widget_item_temp_high,maxtemp);
|
||||
rv.setTextViewText(R.id.widget_item_temp_low,mintemp);
|
||||
|
||||
Intent fillInIntent = new Intent();
|
||||
fillInIntent.putExtra("currentForcast", forecastList.get(i));
|
||||
rv.setOnClickFillInIntent(R.id.widget_item_layout, fillInIntent);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteViews getLoadingView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather.AppWidget;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.TaskStackBuilder;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.CurrentForecast;
|
||||
import com.appttude.h_mal.atlas_weather.ForecastItem;
|
||||
import com.appttude.h_mal.atlas_weather.FurtherInfoActivity;
|
||||
import com.appttude.h_mal.atlas_weather.R;
|
||||
import com.appttude.h_mal.atlas_weather.dbfiles.ForecastDBHelper;
|
||||
import com.appttude.h_mal.atlas_weather.getLatLong;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.changeToInt;
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.getImageResource;
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.getLocationName;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.UriBuilder;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.createUrl;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.extractFeatureFromJson;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.makeHttpRequest;
|
||||
import static com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry.COLUMN_FORECAST_NAME;
|
||||
import static com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry.COLUMN_WIDGET_FORECAST_ITEM;
|
||||
import static com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry.TABLE_NAME_WIDGET;
|
||||
|
||||
/**
|
||||
* Implementation of App Widget functionality.
|
||||
*/
|
||||
public class NewAppWidget extends AppWidgetProvider{
|
||||
|
||||
private static String TAG = NewAppWidget.class.getSimpleName();
|
||||
private static int request = 0;
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
// There may be multiple widgets active, so update all of them
|
||||
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
getLatLong.configLatLong(context);
|
||||
forecastAsyncTask task = new forecastAsyncTask(context,appWidgetManager,appWidgetId);
|
||||
task.execute(UriBuilder(6));
|
||||
Log.i(TAG, "onUpdate: widget onUpdate called at " + getCurrentTimeUsingDate());
|
||||
|
||||
}
|
||||
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
|
||||
try {
|
||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||
|
||||
ComponentName thisAppWidget = new ComponentName(context.getPackageName(), NewAppWidget.class.getName());
|
||||
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
|
||||
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_listview);
|
||||
|
||||
Log.i(TAG, "onEnabled: called at " + getCurrentTimeUsingDate());
|
||||
}catch (Exception e){
|
||||
Log.e(TAG, "onEnabled: ", e);
|
||||
}
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
// getLatLong.configLatLong(context);
|
||||
// forecastAsyncTask task = new forecastAsyncTask();
|
||||
// task.execute(UriBuilder(5));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(Context context) {
|
||||
// Enter relevant functionality for when the last widget is disabled
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
if (intent.getAction().equals(
|
||||
AppWidgetManager.ACTION_APPWIDGET_UPDATE)) {
|
||||
|
||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||
|
||||
ComponentName thisAppWidget = new ComponentName(context.getPackageName(), NewAppWidget.class.getName());
|
||||
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
|
||||
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_listview);
|
||||
Log.i(TAG, "onReceive: widget onReceive called at " + getCurrentTimeUsingDate());
|
||||
|
||||
}
|
||||
|
||||
super.onReceive(context, intent);
|
||||
}
|
||||
|
||||
static class forecastAsyncTask extends AsyncTask<String,Void,String>{
|
||||
|
||||
private Context context;
|
||||
AppWidgetManager appWidgetManager;
|
||||
int appWidgetId;
|
||||
|
||||
public forecastAsyncTask(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
|
||||
this.context = context;
|
||||
this.appWidgetManager = appWidgetManager;
|
||||
this.appWidgetId = appWidgetId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... urlString) {
|
||||
Log.i(TAG, "doInBackground: started at " + getCurrentTimeUsingDate());
|
||||
String jsonResponse = null;
|
||||
|
||||
if (urlString.length < 1 || urlString[0] == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
URL url = createUrl(urlString[0]);
|
||||
jsonResponse = makeHttpRequest(url);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e( TAG, "Problem making the HTTP request.", e);
|
||||
}
|
||||
|
||||
return jsonResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
super.onPostExecute(result);
|
||||
|
||||
Log.i(TAG, "onPostExecute: SQL data result: " + result);
|
||||
|
||||
if(result == null){
|
||||
Log.i(TAG, "onPostExecute: result null");
|
||||
}
|
||||
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
|
||||
|
||||
Intent intentUpdate = new Intent(context, NewAppWidget.class);
|
||||
intentUpdate.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
|
||||
int[] idArray = new int[]{appWidgetId};
|
||||
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray);
|
||||
|
||||
PendingIntent pendingUpdate = PendingIntent.getBroadcast(
|
||||
context, request++, intentUpdate,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
// float opacity = 0.3f; //opacity = 0: fully transparent, opacity = 1: no transparancy
|
||||
// int backgroundColor = 0x000000; //background color (here black)
|
||||
// views.setInt( R.id.whole_widget_view, "setBackgroundColor", (int)(opacity * 0xFF) << 24 | backgroundColor);
|
||||
|
||||
if (result != null) {
|
||||
|
||||
ForecastDBHelper forecastsDbhelper = new ForecastDBHelper(context);
|
||||
SQLiteDatabase database = forecastsDbhelper.getWritableDatabase();
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_FORECAST_NAME, "Current");
|
||||
values.put(COLUMN_WIDGET_FORECAST_ITEM, result);
|
||||
|
||||
String[] projection = {COLUMN_FORECAST_NAME, COLUMN_WIDGET_FORECAST_ITEM};
|
||||
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = database.query(TABLE_NAME_WIDGET,
|
||||
projection,
|
||||
COLUMN_FORECAST_NAME + " IS ?",
|
||||
new String[]{"Current"},
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onPostExecute: ", e);
|
||||
} finally {
|
||||
if(cursor != null){
|
||||
while (cursor.moveToNext()){
|
||||
String currentDB = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_WIDGET_FORECAST_ITEM));
|
||||
Log.i(TAG, "onPostExecute: current db: " + currentDB);
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor != null && cursor.getCount() > 0) {
|
||||
database.update(TABLE_NAME_WIDGET, values, COLUMN_FORECAST_NAME + " IS ?", new String[]{"Current"});
|
||||
Log.i(TAG, "onPostExecute: attempted to update sql, size:" + cursor.getCount());
|
||||
cursor.close();
|
||||
} else {
|
||||
database.insert(TABLE_NAME_WIDGET, null, values);
|
||||
Log.i(TAG, "onPostExecute: attempted to insert sql");
|
||||
}
|
||||
}
|
||||
|
||||
ForecastItem forecastItem = extractFeatureFromJson(result);
|
||||
CurrentForecast cf = forecastItem.getCurrentForecast();
|
||||
|
||||
Intent intent = new Intent(context, WidgetRemoteViewsService.class);
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
|
||||
|
||||
SharedPreferences mSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String temp;
|
||||
String unit;
|
||||
if (mSettings.getString("temp_units","").equals("°F")){
|
||||
temp =changeToInt(cf.getTemp_f());
|
||||
unit = "°F";
|
||||
}else {
|
||||
temp = changeToInt(cf.getTemp_c());
|
||||
unit = "°C";
|
||||
}
|
||||
|
||||
views.setRemoteAdapter(R.id.widget_listview, intent);
|
||||
views.setTextViewText(R.id.widget_main_temp,temp);
|
||||
views.setTextViewText(R.id.widget_feel_temp,unit);
|
||||
views.setTextViewText(R.id.dash," / ");
|
||||
views.setTextViewText(R.id.widget_item_temp_high,changeToInt(forecastItem.getForecastArrayList().get(0).getMaxtemp_c())+"°");
|
||||
views.setTextViewText(R.id.widget_item_temp_low,changeToInt(forecastItem.getForecastArrayList().get(0).getMintemp_c())+"°");
|
||||
views.setTextViewText(R.id.widget_current_location,getLocationName(context,cf.getLatitude(),cf.getLongitude()));
|
||||
views.setImageViewResource(R.id.location_icon, R.drawable.location_flag);
|
||||
views.setImageViewResource(R.id.widget_current_icon,
|
||||
getImageResource(cf.getIconURL(),context));
|
||||
|
||||
Intent clickIntentTemplate = new Intent(context, FurtherInfoActivity.class);
|
||||
PendingIntent clickPendingIntentTemplate = TaskStackBuilder.create(context)
|
||||
.addNextIntentWithParentStack(clickIntentTemplate)
|
||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
views.setPendingIntentTemplate(R.id.widget_listview, clickPendingIntentTemplate);
|
||||
|
||||
views.setOnClickPendingIntent(R.id.widget_current_icon, pendingUpdate);
|
||||
views.setOnClickPendingIntent(R.id.widget_current_location,pendingUpdate);
|
||||
|
||||
// Instruct the widget manager to update the widget
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview);
|
||||
Log.i(TAG, "onPostExecute: finished at " + getCurrentTimeUsingDate());
|
||||
}
|
||||
else {
|
||||
Log.i(TAG, "onPostExecute: null part executed");
|
||||
|
||||
views.setTextViewText(R.id.widget_current_location,"Refresh");
|
||||
views.setImageViewResource(R.id.widget_current_icon, R.drawable.widget_error_icon);
|
||||
views.setImageViewResource(R.id.location_icon, R.drawable.refreshing);
|
||||
|
||||
views.setOnClickPendingIntent(R.id.widget_current_icon, pendingUpdate);
|
||||
views.setOnClickPendingIntent(R.id.widget_current_location,pendingUpdate);
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
// appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview);
|
||||
//add a listener to the view
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getCurrentTimeUsingDate() {
|
||||
Date date = new Date();
|
||||
String strDateFormat = "hh:mm:ss.SSS";
|
||||
DateFormat dateFormat = new SimpleDateFormat(strDateFormat);
|
||||
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather.AppWidget;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.widget.RemoteViewsService;
|
||||
|
||||
|
||||
public class WidgetRemoteViewsService extends RemoteViewsService{
|
||||
|
||||
@Override
|
||||
public RemoteViewsFactory onGetViewFactory(Intent intent) {
|
||||
return new MyWidgetRemoteViewsFactory(getApplicationContext(),intent);
|
||||
}
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class CurrentForecast implements Parcelable{
|
||||
|
||||
private String location;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private int last_updated_epoch;
|
||||
private Double temp_c;
|
||||
private Double temp_f;
|
||||
private String condition_text;
|
||||
private String iconURL;
|
||||
private Double wind_mph;
|
||||
private Double wind_kph;
|
||||
private String wind_dir;
|
||||
private Double pressure_mb;
|
||||
private Double pressure_in;
|
||||
private Double precip_mm;
|
||||
private Double precip_in;
|
||||
private Double humidity;
|
||||
private Double cloud;
|
||||
private Double feelslike_c;
|
||||
private Double feelslike_f;
|
||||
private Double vis_km;
|
||||
private Double vis_miles;
|
||||
|
||||
public CurrentForecast() {
|
||||
}
|
||||
|
||||
public CurrentForecast(String location, Double latitude, Double longitude, int last_updated_epoch,
|
||||
Double temp_c, Double temp_f, String condition_text, String iconURL, Double wind_mph,
|
||||
Double wind_kph, String wind_dir, Double pressure_mb, Double pressure_in, Double precip_mm,
|
||||
Double precip_in, Double humidity, Double cloud, Double feelslike_c, Double feelslike_f,
|
||||
Double vis_km, Double vis_miles) {
|
||||
this.location = location;
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
this.last_updated_epoch = last_updated_epoch;
|
||||
this.temp_c = temp_c;
|
||||
this.temp_f = temp_f;
|
||||
this.condition_text = condition_text;
|
||||
this.iconURL = iconURL;
|
||||
this.wind_mph = wind_mph;
|
||||
this.wind_kph = wind_kph;
|
||||
this.wind_dir = wind_dir;
|
||||
this.pressure_mb = pressure_mb;
|
||||
this.pressure_in = pressure_in;
|
||||
this.precip_mm = precip_mm;
|
||||
this.precip_in = precip_in;
|
||||
this.humidity = humidity;
|
||||
this.cloud = cloud;
|
||||
this.feelslike_c = feelslike_c;
|
||||
this.feelslike_f = feelslike_f;
|
||||
this.vis_km = vis_km;
|
||||
this.vis_miles = vis_miles;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public Double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public Double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public int getLast_updated_epoch() {
|
||||
return last_updated_epoch;
|
||||
}
|
||||
|
||||
public Double getTemp_c() {
|
||||
return temp_c;
|
||||
}
|
||||
|
||||
public Double getTemp_f() {
|
||||
return temp_f;
|
||||
}
|
||||
|
||||
public String getCondition_text() {
|
||||
return condition_text;
|
||||
}
|
||||
|
||||
public String getIconURL() {
|
||||
return iconURL;
|
||||
}
|
||||
|
||||
public Double getWind_mph() {
|
||||
return wind_mph;
|
||||
}
|
||||
|
||||
public Double getWind_kph() {
|
||||
return wind_kph;
|
||||
}
|
||||
|
||||
public String getWind_dir() {
|
||||
return wind_dir;
|
||||
}
|
||||
|
||||
public Double getPressure_mb() {
|
||||
return pressure_mb;
|
||||
}
|
||||
|
||||
public Double getPressure_in() {
|
||||
return pressure_in;
|
||||
}
|
||||
|
||||
public Double getPrecip_mm() {
|
||||
return precip_mm;
|
||||
}
|
||||
|
||||
public Double getPrecip_in() {
|
||||
return precip_in;
|
||||
}
|
||||
|
||||
public Double getHumidity() {
|
||||
return humidity;
|
||||
}
|
||||
|
||||
public Double getCloud() {
|
||||
return cloud;
|
||||
}
|
||||
|
||||
public Double getFeelslike_c() {
|
||||
return feelslike_c;
|
||||
}
|
||||
|
||||
public Double getFeelslike_f() {
|
||||
return feelslike_f;
|
||||
}
|
||||
|
||||
public Double getVis_km() {
|
||||
return vis_km;
|
||||
}
|
||||
|
||||
public Double getVis_miles() {
|
||||
return vis_miles;
|
||||
}
|
||||
|
||||
public static Creator<CurrentForecast> getCREATOR() {
|
||||
return CREATOR;
|
||||
}
|
||||
|
||||
protected CurrentForecast(Parcel in) {
|
||||
location = in.readString();
|
||||
if (in.readByte() == 0) {
|
||||
latitude = null;
|
||||
} else {
|
||||
latitude = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
longitude = null;
|
||||
} else {
|
||||
longitude = in.readDouble();
|
||||
}
|
||||
last_updated_epoch = in.readInt();
|
||||
if (in.readByte() == 0) {
|
||||
temp_c = null;
|
||||
} else {
|
||||
temp_c = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
temp_f = null;
|
||||
} else {
|
||||
temp_f = in.readDouble();
|
||||
}
|
||||
condition_text = in.readString();
|
||||
iconURL = in.readString();
|
||||
if (in.readByte() == 0) {
|
||||
wind_mph = null;
|
||||
} else {
|
||||
wind_mph = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
wind_kph = null;
|
||||
} else {
|
||||
wind_kph = in.readDouble();
|
||||
}
|
||||
wind_dir = in.readString();
|
||||
if (in.readByte() == 0) {
|
||||
pressure_mb = null;
|
||||
} else {
|
||||
pressure_mb = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
pressure_in = null;
|
||||
} else {
|
||||
pressure_in = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
precip_mm = null;
|
||||
} else {
|
||||
precip_mm = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
precip_in = null;
|
||||
} else {
|
||||
precip_in = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
humidity = null;
|
||||
} else {
|
||||
humidity = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
cloud = null;
|
||||
} else {
|
||||
cloud = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
feelslike_c = null;
|
||||
} else {
|
||||
feelslike_c = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
feelslike_f = null;
|
||||
} else {
|
||||
feelslike_f = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
vis_km = null;
|
||||
} else {
|
||||
vis_km = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
vis_miles = null;
|
||||
} else {
|
||||
vis_miles = in.readDouble();
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<CurrentForecast> CREATOR = new Creator<CurrentForecast>() {
|
||||
@Override
|
||||
public CurrentForecast createFromParcel(Parcel in) {
|
||||
return new CurrentForecast(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrentForecast[] newArray(int size) {
|
||||
return new CurrentForecast[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int i) {
|
||||
parcel.writeString(location);
|
||||
if (latitude == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(latitude);
|
||||
}
|
||||
if (longitude == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(longitude);
|
||||
}
|
||||
parcel.writeInt(last_updated_epoch);
|
||||
if (temp_c == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(temp_c);
|
||||
}
|
||||
if (temp_f == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(temp_f);
|
||||
}
|
||||
parcel.writeString(condition_text);
|
||||
parcel.writeString(iconURL);
|
||||
if (wind_mph == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(wind_mph);
|
||||
}
|
||||
if (wind_kph == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(wind_kph);
|
||||
}
|
||||
parcel.writeString(wind_dir);
|
||||
if (pressure_mb == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(pressure_mb);
|
||||
}
|
||||
if (pressure_in == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(pressure_in);
|
||||
}
|
||||
if (precip_mm == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(precip_mm);
|
||||
}
|
||||
if (precip_in == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(precip_in);
|
||||
}
|
||||
if (humidity == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(humidity);
|
||||
}
|
||||
if (cloud == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(cloud);
|
||||
}
|
||||
if (feelslike_c == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(feelslike_c);
|
||||
}
|
||||
if (feelslike_f == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(feelslike_f);
|
||||
}
|
||||
if (vis_km == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(vis_km);
|
||||
}
|
||||
if (vis_miles == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(vis_miles);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.changeToInt;
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.getImageResource;
|
||||
|
||||
public class CurrentForecastAdapter extends ArrayAdapter<ForecastItem> {
|
||||
Context context;
|
||||
private SharedPreferences mSettings;
|
||||
|
||||
public CurrentForecastAdapter(@NonNull Context context, @NonNull List<ForecastItem> objects) {
|
||||
super(context, 0, objects);
|
||||
this.context = context;
|
||||
|
||||
mSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
View listItemView = convertView;
|
||||
|
||||
if (listItemView == null) {
|
||||
listItemView = LayoutInflater.from(getContext()).inflate(R.layout.db_list_item, parent,false);
|
||||
}
|
||||
|
||||
CurrentForecast currentForecast = null;
|
||||
try {
|
||||
currentForecast = getItem(position).getCurrentForecast();
|
||||
}catch (Exception e){
|
||||
Log.e(getClass().getSimpleName(), "error: ", e);
|
||||
}finally {
|
||||
if (currentForecast != null){
|
||||
TextView location = listItemView.findViewById(R.id.db_location);
|
||||
location.setText(currentForecast.getLocation());
|
||||
|
||||
TextView conditionTV = listItemView.findViewById(R.id.db_condition);
|
||||
conditionTV.setText(currentForecast.getCondition_text());
|
||||
|
||||
ImageView weatherIV = listItemView.findViewById(R.id.db_icon);
|
||||
weatherIV.setImageResource(getImageResource(currentForecast.getIconURL(), context));
|
||||
|
||||
TextView mainTempTV = listItemView.findViewById(R.id.db_main_temp);
|
||||
TextView tempUnit = listItemView.findViewById(R.id.db_minor_temp);
|
||||
if (mSettings.getString("temp_units","").equals("°F")){
|
||||
mainTempTV.setText(changeToInt(currentForecast.getTemp_f()));
|
||||
tempUnit.setText("°F");
|
||||
}else {
|
||||
mainTempTV.setText(changeToInt(currentForecast.getTemp_c()));
|
||||
tempUnit.setText("°C");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return listItemView;
|
||||
}
|
||||
|
||||
private void openWorldItem(ForecastItem forcast){
|
||||
Intent i = new Intent(context,WorldItemActivity.class);
|
||||
i.putExtra("ForecastItem",forcast);
|
||||
context.startActivity(i);
|
||||
}
|
||||
}
|
||||
@@ -1,351 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Created by h_mal on 19/04/2018.
|
||||
*/
|
||||
|
||||
public class Forecast implements Parcelable {
|
||||
|
||||
private Long date_epoch;
|
||||
private Double maxtemp_c;
|
||||
private Double maxtemp_f;
|
||||
private Double mintemp_c;
|
||||
private Double mintemp_f;
|
||||
private Double avgtemp_c;
|
||||
private Double avgtemp_f;
|
||||
private Double maxwind_mph;
|
||||
private Double maxwind_kph;
|
||||
private Double totalprecip_mm;
|
||||
private Double totalprecip_in;
|
||||
private Double avgvis_km;
|
||||
private Double avgvis_miles;
|
||||
private Double avghumidity;
|
||||
private String forecast_text;
|
||||
private String iconURL;
|
||||
private Double uv;
|
||||
private String sunrise;
|
||||
private String sunset;
|
||||
private String moonrise;
|
||||
private String moonset;
|
||||
|
||||
public Forecast(Long date_epoch, Double maxtemp_c, Double maxtemp_f, Double mintemp_c, Double mintemp_f, Double avgtemp_c, Double avgtemp_f,
|
||||
Double maxwind_mph, Double maxwind_kph, Double totalprecip_mm, Double totalprecip_in, Double avgvis_km, Double avgvis_miles,
|
||||
Double avghumidity, String forecast_text, String iconURL, Double uv, String sunrise, String sunset, String moonrise, String moonset) {
|
||||
this.date_epoch = date_epoch;
|
||||
this.maxtemp_c = maxtemp_c;
|
||||
this.maxtemp_f = maxtemp_f;
|
||||
this.mintemp_c = mintemp_c;
|
||||
this.mintemp_f = mintemp_f;
|
||||
this.avgtemp_c = avgtemp_c;
|
||||
this.avgtemp_f = avgtemp_f;
|
||||
this.maxwind_mph = maxwind_mph;
|
||||
this.maxwind_kph = maxwind_kph;
|
||||
this.totalprecip_mm = totalprecip_mm;
|
||||
this.totalprecip_in = totalprecip_in;
|
||||
this.avgvis_km = avgvis_km;
|
||||
this.avgvis_miles = avgvis_miles;
|
||||
this.avghumidity = avghumidity;
|
||||
this.forecast_text = forecast_text;
|
||||
this.iconURL = iconURL;
|
||||
this.uv = uv;
|
||||
this.sunrise = sunrise;
|
||||
this.sunset = sunset;
|
||||
this.moonrise = moonrise;
|
||||
this.moonset = moonset;
|
||||
}
|
||||
|
||||
public Forecast(Long date_epoch, Double avgtemp_c,String forecast_text, String iconURL) {
|
||||
this.date_epoch = date_epoch;
|
||||
this.avgtemp_c = avgtemp_c;
|
||||
this.forecast_text = forecast_text;
|
||||
this.iconURL = iconURL;
|
||||
}
|
||||
|
||||
protected Forecast(Parcel in) {
|
||||
if (in.readByte() == 0) {
|
||||
date_epoch = null;
|
||||
} else {
|
||||
date_epoch = in.readLong();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
maxtemp_c = null;
|
||||
} else {
|
||||
maxtemp_c = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
maxtemp_f = null;
|
||||
} else {
|
||||
maxtemp_f = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
mintemp_c = null;
|
||||
} else {
|
||||
mintemp_c = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
mintemp_f = null;
|
||||
} else {
|
||||
mintemp_f = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
avgtemp_c = null;
|
||||
} else {
|
||||
avgtemp_c = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
avgtemp_f = null;
|
||||
} else {
|
||||
avgtemp_f = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
maxwind_mph = null;
|
||||
} else {
|
||||
maxwind_mph = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
maxwind_kph = null;
|
||||
} else {
|
||||
maxwind_kph = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
totalprecip_mm = null;
|
||||
} else {
|
||||
totalprecip_mm = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
totalprecip_in = null;
|
||||
} else {
|
||||
totalprecip_in = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
avgvis_km = null;
|
||||
} else {
|
||||
avgvis_km = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
avgvis_miles = null;
|
||||
} else {
|
||||
avgvis_miles = in.readDouble();
|
||||
}
|
||||
if (in.readByte() == 0) {
|
||||
avghumidity = null;
|
||||
} else {
|
||||
avghumidity = in.readDouble();
|
||||
}
|
||||
forecast_text = in.readString();
|
||||
iconURL = in.readString();
|
||||
if (in.readByte() == 0) {
|
||||
uv = null;
|
||||
} else {
|
||||
uv = in.readDouble();
|
||||
}
|
||||
sunrise = in.readString();
|
||||
sunset = in.readString();
|
||||
moonrise = in.readString();
|
||||
moonset = in.readString();
|
||||
}
|
||||
|
||||
public static final Creator<Forecast> CREATOR = new Creator<Forecast>() {
|
||||
@Override
|
||||
public Forecast createFromParcel(Parcel in) {
|
||||
return new Forecast(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forecast[] newArray(int size) {
|
||||
return new Forecast[size];
|
||||
}
|
||||
};
|
||||
|
||||
public Long getDate_epoch() {
|
||||
return date_epoch;
|
||||
}
|
||||
|
||||
public Double getMaxtemp_c() {
|
||||
return maxtemp_c;
|
||||
}
|
||||
|
||||
public Double getMaxtemp_f() {
|
||||
return maxtemp_f;
|
||||
}
|
||||
|
||||
public Double getMintemp_c() {
|
||||
return mintemp_c;
|
||||
}
|
||||
|
||||
public Double getMintemp_f() {
|
||||
return mintemp_f;
|
||||
}
|
||||
|
||||
public Double getAvgtemp_c() {
|
||||
return avgtemp_c;
|
||||
}
|
||||
|
||||
public Double getAvgtemp_f() {
|
||||
return avgtemp_f;
|
||||
}
|
||||
|
||||
public Double getMaxwind_mph() {
|
||||
return maxwind_mph;
|
||||
}
|
||||
|
||||
public Double getMaxwind_kph() {
|
||||
return maxwind_kph;
|
||||
}
|
||||
|
||||
public Double getTotalprecip_mm() {
|
||||
return totalprecip_mm;
|
||||
}
|
||||
|
||||
public Double getTotalprecip_in() {
|
||||
return totalprecip_in;
|
||||
}
|
||||
|
||||
public Double getAvgvis_km() {
|
||||
return avgvis_km;
|
||||
}
|
||||
|
||||
public Double getAvgvis_miles() {
|
||||
return avgvis_miles;
|
||||
}
|
||||
|
||||
public Double getAvghumidity() {
|
||||
return avghumidity;
|
||||
}
|
||||
|
||||
public String getForecast_text() {
|
||||
return forecast_text;
|
||||
}
|
||||
|
||||
public String getIconURL() {
|
||||
return iconURL;
|
||||
}
|
||||
|
||||
public Double getUv() {
|
||||
return uv;
|
||||
}
|
||||
|
||||
public String getSunrise() {
|
||||
return sunrise;
|
||||
}
|
||||
|
||||
public String getSunset() {
|
||||
return sunset;
|
||||
}
|
||||
|
||||
public String getMoonrise() {
|
||||
return moonrise;
|
||||
}
|
||||
|
||||
public String getMoonset() {
|
||||
return moonset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int i) {
|
||||
if (date_epoch == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeLong(date_epoch);
|
||||
}
|
||||
if (maxtemp_c == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(maxtemp_c);
|
||||
}
|
||||
if (maxtemp_f == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(maxtemp_f);
|
||||
}
|
||||
if (mintemp_c == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(mintemp_c);
|
||||
}
|
||||
if (mintemp_f == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(mintemp_f);
|
||||
}
|
||||
if (avgtemp_c == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(avgtemp_c);
|
||||
}
|
||||
if (avgtemp_f == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(avgtemp_f);
|
||||
}
|
||||
if (maxwind_mph == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(maxwind_mph);
|
||||
}
|
||||
if (maxwind_kph == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(maxwind_kph);
|
||||
}
|
||||
if (totalprecip_mm == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(totalprecip_mm);
|
||||
}
|
||||
if (totalprecip_in == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(totalprecip_in);
|
||||
}
|
||||
if (avgvis_km == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(avgvis_km);
|
||||
}
|
||||
if (avgvis_miles == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(avgvis_miles);
|
||||
}
|
||||
if (avghumidity == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(avghumidity);
|
||||
}
|
||||
parcel.writeString(forecast_text);
|
||||
parcel.writeString(iconURL);
|
||||
if (uv == null) {
|
||||
parcel.writeByte((byte) 0);
|
||||
} else {
|
||||
parcel.writeByte((byte) 1);
|
||||
parcel.writeDouble(uv);
|
||||
}
|
||||
parcel.writeString(sunrise);
|
||||
parcel.writeString(sunset);
|
||||
parcel.writeString(moonrise);
|
||||
parcel.writeString(moonset);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ForecastItem implements Parcelable {
|
||||
|
||||
private CurrentForecast currentForecast;
|
||||
private List<Forecast> forecastArrayList;
|
||||
|
||||
public ForecastItem(CurrentForecast currentForecast, List<Forecast> forecastArrayList) {
|
||||
this.currentForecast = currentForecast;
|
||||
this.forecastArrayList = forecastArrayList;
|
||||
}
|
||||
|
||||
protected ForecastItem(Parcel in) {
|
||||
currentForecast = in.readParcelable(CurrentForecast.class.getClassLoader());
|
||||
forecastArrayList = in.createTypedArrayList(Forecast.CREATOR);
|
||||
}
|
||||
|
||||
public static final Creator<ForecastItem> CREATOR = new Creator<ForecastItem>() {
|
||||
@Override
|
||||
public ForecastItem createFromParcel(Parcel in) {
|
||||
return new ForecastItem(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForecastItem[] newArray(int size) {
|
||||
return new ForecastItem[size];
|
||||
}
|
||||
};
|
||||
|
||||
public CurrentForecast getCurrentForecast() {
|
||||
return currentForecast;
|
||||
}
|
||||
|
||||
public List<Forecast> getForecastArrayList() {
|
||||
return forecastArrayList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int i) {
|
||||
parcel.writeParcelable(currentForecast, i);
|
||||
parcel.writeTypedList(forecastArrayList);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.createUrl;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.extractFeatureFromJson;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.makeHttpRequest;
|
||||
|
||||
public class ForecastLoader extends android.support.v4.content.AsyncTaskLoader<List<ForecastItem>> {
|
||||
|
||||
private ArrayList<String> mUrl;
|
||||
|
||||
public ForecastLoader(Context context, ArrayList<String> url) {
|
||||
super(context);
|
||||
this.mUrl = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
forceLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ForecastItem> loadInBackground() {
|
||||
if (mUrl == null) {
|
||||
Log.i("", "loadInBackground: " + "null");
|
||||
return null;
|
||||
}
|
||||
|
||||
String json = null;
|
||||
List<ForecastItem> forecastItems = new ArrayList<ForecastItem>();
|
||||
|
||||
for (int i = 0; i < mUrl.size(); i++) {
|
||||
try {
|
||||
URL url = createUrl(mUrl.get(i));
|
||||
json = makeHttpRequest(url);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e("", "Problem making the HTTP request.", e);
|
||||
}finally {
|
||||
if (!TextUtils.isEmpty(json)) {
|
||||
forecastItems.add(extractFeatureFromJson(json));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forecastItems.size() < 1){
|
||||
return null;
|
||||
}
|
||||
|
||||
return forecastItems;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract;
|
||||
import com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.networkInfo;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.UriBuilder;
|
||||
|
||||
public class Fragment_Two extends Fragment implements android.support.v4.app.LoaderManager.LoaderCallbacks<List<ForecastItem>>{
|
||||
|
||||
private static final int NEWS_LOADER_ID = 1;
|
||||
private String TAG = getClass().getSimpleName();
|
||||
|
||||
CurrentForecastAdapter mAdapter;
|
||||
ProgressBar pb_2;
|
||||
LinearLayout emptyView;
|
||||
ListView lv;
|
||||
android.support.v4.app.LoaderManager loaderManager;
|
||||
|
||||
public Fragment_Two() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment__two, container, false);
|
||||
|
||||
emptyView = rootView.findViewById(R.id.emptyView);
|
||||
pb_2 = rootView.findViewById(R.id.progressBar2);
|
||||
|
||||
lv = rootView.findViewById(R.id.listview);
|
||||
|
||||
final LoaderManager.LoaderCallbacks callbacks = this;
|
||||
loaderManager = getLoaderManager();
|
||||
loaderManager.initLoader(NEWS_LOADER_ID, null, callbacks);
|
||||
|
||||
mAdapter = new CurrentForecastAdapter(getActivity(), new ArrayList<ForecastItem>());
|
||||
lv.setAdapter(mAdapter);
|
||||
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> adapterView, View view, final int i, long l) {
|
||||
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle("Delete?")
|
||||
.setMessage("Continue?")
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface arg0, int arg1) {
|
||||
Cursor cursor = getActivity().getContentResolver().query(ForecastContract.ForecastEntry.CONTENT_URI,
|
||||
new String[]{ForecastContract.ForecastEntry._ID,ForecastContract.ForecastEntry.COLUMN_FORECAST_NAME},
|
||||
null,null,null);
|
||||
|
||||
cursor.moveToPosition(i);
|
||||
int id = cursor.getInt(cursor.getColumnIndexOrThrow(ForecastContract.ForecastEntry._ID));
|
||||
String selection = ForecastContract.ForecastEntry._ID + "=?";
|
||||
getActivity().getContentResolver().delete(ForecastContract.ForecastEntry.CONTENT_URI,
|
||||
selection, new String[]{id + ""});
|
||||
|
||||
loaderManager.restartLoader(NEWS_LOADER_ID, null, callbacks);
|
||||
}
|
||||
}).create().show();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
Intent intent = new Intent(getContext(),WorldItemActivity.class);
|
||||
intent.putExtra("ForecastItem",mAdapter.getItem(i));
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
FloatingActionButton fab = rootView.findViewById(R.id.floatingActionButton);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(getActivity(), AddForecast.class);
|
||||
startActivity(i);
|
||||
}
|
||||
});
|
||||
|
||||
if (networkInfo != null && networkInfo.isConnected()) {
|
||||
loaderManager.initLoader(NEWS_LOADER_ID, null, callbacks);
|
||||
} else {
|
||||
// emptyList.setVisibility(View.VISIBLE);
|
||||
// mainPG.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
loaderManager.restartLoader(NEWS_LOADER_ID, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<List<ForecastItem>> onCreateLoader(int id, Bundle args) {
|
||||
pb_2.setVisibility(View.VISIBLE);
|
||||
lv.setVisibility(View.GONE);
|
||||
|
||||
ArrayList<String> entries = new ArrayList<>();
|
||||
|
||||
String[] projection = {ForecastEntry.COLUMN_FORECAST_NAME};
|
||||
Cursor cursor = getActivity().getContentResolver().query(ForecastEntry.CONTENT_URI,
|
||||
projection,null,null,null);
|
||||
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
final String descriptionColumnIndex = cursor.getString(cursor.getColumnIndexOrThrow(ForecastEntry.COLUMN_FORECAST_NAME));
|
||||
entries.add(UriBuilder(descriptionColumnIndex));
|
||||
}
|
||||
}catch (Exception e){
|
||||
Log.e(TAG, "onCreateLoader: ",e );
|
||||
}finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
return new ForecastLoader(getContext(),entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<ForecastItem>> loader, List<ForecastItem> data) {
|
||||
mAdapter.clear();
|
||||
|
||||
TextView t = emptyView.findViewById(R.id.emptyViewText);
|
||||
|
||||
if (data == null){
|
||||
lv.setVisibility(View.GONE);
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
t.setText("Add Items");
|
||||
Log.i(TAG, "onLoadFinished: data null");
|
||||
}
|
||||
|
||||
if(networkInfo == null || !networkInfo.isConnected()){
|
||||
lv.setVisibility(View.GONE);
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
t.setText("Check connection...");
|
||||
}
|
||||
|
||||
if (data != null && !data.isEmpty()) {
|
||||
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
mAdapter.add(data.get(i));
|
||||
}
|
||||
emptyView.setVisibility(View.GONE);
|
||||
Log.i(TAG, "onLoadFinished: data not null");
|
||||
lv.setVisibility(View.VISIBLE);
|
||||
}
|
||||
pb_2.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<ForecastItem>> loader) {
|
||||
pb_2.setVisibility(View.GONE);
|
||||
mAdapter.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.UriBuilder;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.createUrl;
|
||||
import static com.appttude.h_mal.atlas_weather.getLatLong.latitude;
|
||||
import static com.appttude.h_mal.atlas_weather.getLatLong.longitude;
|
||||
|
||||
|
||||
public class Fragment_home extends Fragment implements android.support.v4.app.LoaderManager.LoaderCallbacks<List<ForecastItem>>{
|
||||
|
||||
protected static String APIkey = "2e914b44f94a4f07853100835181104";
|
||||
android.support.v4.app.LoaderManager loaderManager;
|
||||
android.support.v4.app.LoaderManager.LoaderCallbacks callbacks;
|
||||
public static int MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
|
||||
|
||||
private static final int NEWS_LOADER_ID = 1;
|
||||
|
||||
RecyclerView forecastsListView;
|
||||
LinearLayout emptyList;
|
||||
ProgressBar mainPG;
|
||||
Button button;
|
||||
|
||||
private RecyclerViewAdapter mAdapter;
|
||||
SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
|
||||
|
||||
// try {
|
||||
// getLatLong.configLatLong(getContext());
|
||||
// }catch (Exception e){
|
||||
// System.out.println("error msg: " + e);
|
||||
// }
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(getContext(),
|
||||
android.Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
if (!ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
|
||||
android.Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
|
||||
MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
|
||||
}
|
||||
}
|
||||
|
||||
forecastsListView = rootView.findViewById(R.id.forecast_listview);
|
||||
forecastsListView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
emptyList = rootView.findViewById(R.id.emptyView);
|
||||
mainPG = rootView.findViewById(R.id.mainPB);
|
||||
|
||||
emptyList.setVisibility(View.GONE);
|
||||
button = emptyList.findViewById(R.id.emptyViewButton);
|
||||
|
||||
mSwipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh);
|
||||
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
loaderManager.restartLoader(NEWS_LOADER_ID,null,callbacks);
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
loaderManager = getLoaderManager();
|
||||
callbacks = this;
|
||||
|
||||
// if (MainActivity.networkInfo != null && MainActivity.networkInfo.isConnected()) {
|
||||
loaderManager.initLoader(NEWS_LOADER_ID, null, callbacks);
|
||||
// } else {
|
||||
// emptyList.setVisibility(View.VISIBLE);
|
||||
// mainPG.setVisibility(View.GONE);
|
||||
// button.setVisibility(View.GONE);
|
||||
// }
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (requestCode == MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION){
|
||||
if (grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
try {
|
||||
getLatLong.configLatLong(getContext());
|
||||
}catch (Exception e){
|
||||
System.out.println("error msg: " + e);
|
||||
}
|
||||
loaderManager.restartLoader(NEWS_LOADER_ID, null, callbacks);
|
||||
Toast.makeText(getContext(), "Permission granted", Toast.LENGTH_SHORT).show();
|
||||
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Permission denied", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.support.v4.content.Loader<List<ForecastItem>> onCreateLoader(int id, Bundle args) {
|
||||
mainPG.setVisibility(View.VISIBLE);
|
||||
ArrayList<String> entries = new ArrayList<>();
|
||||
try {
|
||||
getLatLong.configLatLong(getContext());
|
||||
}catch (Exception e){
|
||||
System.out.println("error msg: " + e);
|
||||
}finally {
|
||||
if (latitude != null){
|
||||
URL url = createUrl(UriBuilder(7));
|
||||
entries.add(url.toString());
|
||||
}
|
||||
|
||||
}
|
||||
// getLatLong.configLatLong(getContext());
|
||||
//// Toast.makeText(getContext(), "" + getLatLong.latitude.toString(), Toast.LENGTH_SHORT).show();
|
||||
// URL url = createUrl(UriBuilder());
|
||||
// ArrayList<String> entries = new ArrayList<>();
|
||||
// entries.add(url.toString());
|
||||
|
||||
return new ForecastLoader(getContext(),entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(android.support.v4.content.Loader<List<ForecastItem>> loader, List<ForecastItem> data) {
|
||||
|
||||
if (mSwipeRefreshLayout.isRefreshing()){
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(getContext(),
|
||||
android.Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
!= PackageManager.PERMISSION_GRANTED){
|
||||
emptyList.setVisibility(View.VISIBLE);
|
||||
TextView tv = emptyList.findViewById(R.id.emptyViewText);
|
||||
tv.setText("Location Required");
|
||||
|
||||
button.setVisibility(View.VISIBLE);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
grantPermission();
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
if (data == null){
|
||||
forecastsListView.setVisibility(View.GONE);
|
||||
emptyList.setVisibility(View.VISIBLE);
|
||||
button.setVisibility(View.GONE);
|
||||
Toast.makeText(getContext(), "Could not retrieve data", Toast.LENGTH_SHORT).show();
|
||||
}else{
|
||||
mAdapter = new RecyclerViewAdapter(getContext(),data.get(0),latitude,longitude);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
forecastsListView.setAdapter(mAdapter);
|
||||
forecastsListView.setVisibility(View.VISIBLE);
|
||||
emptyList.setVisibility(View.GONE);
|
||||
button.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mainPG.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(android.support.v4.content.Loader<List<ForecastItem>> loader) {
|
||||
loader.reset();
|
||||
}
|
||||
|
||||
public void grantPermission(){
|
||||
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
|
||||
MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
|
||||
loaderManager.restartLoader(NEWS_LOADER_ID, null, callbacks);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.changeToInt;
|
||||
|
||||
public class FurtherInfoActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_further_info);
|
||||
|
||||
Intent mIntent = getIntent();
|
||||
Forecast forecast = mIntent.getParcelableExtra("currentForcast");
|
||||
|
||||
SharedPreferences mSettings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
TextView maxTemp = findViewById(R.id.maxtemp);
|
||||
TextView averageTemp = findViewById(R.id.averagetemp);
|
||||
TextView minimumTemp = findViewById(R.id.minimumtemp);
|
||||
TextView windText = findViewById(R.id.windtext);
|
||||
TextView precipText = findViewById(R.id.preciptext);
|
||||
TextView humidityText = findViewById(R.id.humiditytext);
|
||||
TextView uvText = findViewById(R.id.uvtext);
|
||||
TextView sunriseText = findViewById(R.id.sunrisetext);
|
||||
TextView sunsetText = findViewById(R.id.sunsettext);
|
||||
|
||||
if (mSettings.getString("temp_units","").equals("°F")){
|
||||
maxTemp.setText(changeToInt(forecast.getMaxtemp_f()) + mSettings.getString("temp_units","°F"));
|
||||
averageTemp.setText(changeToInt(forecast.getAvgtemp_f())+mSettings.getString("temp_units","°F"));
|
||||
minimumTemp.setText(changeToInt(forecast.getMintemp_f())+mSettings.getString("temp_units","°F"));
|
||||
|
||||
}else {
|
||||
maxTemp.setText(changeToInt(forecast.getMaxtemp_c())+mSettings.getString("temp_units","°C"));
|
||||
averageTemp.setText(changeToInt(forecast.getAvgtemp_c())+mSettings.getString("temp_units","°C"));
|
||||
minimumTemp.setText(changeToInt(forecast.getMintemp_c())+mSettings.getString("temp_units","°C"));
|
||||
}
|
||||
|
||||
if (mSettings.getString("wind_units","").equals("mph")){
|
||||
windText.setText(String.valueOf(forecast.getMaxwind_mph()+mSettings.getString("wind_units","mhp")));
|
||||
}else {
|
||||
windText.setText(String.valueOf(forecast.getMaxwind_kph()+mSettings.getString("wind_units","kph")));
|
||||
}
|
||||
|
||||
if (mSettings.getString("precip_units","").equals("in")){
|
||||
precipText.setText(String.valueOf(forecast.getTotalprecip_in()+mSettings.getString("precip_units","inches")));
|
||||
}else {
|
||||
precipText.setText(String.valueOf(forecast.getTotalprecip_mm()+ mSettings.getString("precip_units","mm")));
|
||||
}
|
||||
|
||||
humidityText.setText(String.valueOf(forecast.getAvghumidity()));
|
||||
uvText.setText(String.valueOf(forecast.getUv()));
|
||||
sunriseText.setText(forecast.getSunrise());
|
||||
sunsetText.setText(forecast.getSunset());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class InfoActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.info_dialog_layout);
|
||||
|
||||
List<infopageItem> infopageItemList = new ArrayList<>();
|
||||
infopageItemList.add(new infopageItem(R.drawable.day_305,
|
||||
"Weather data and icons provided by: ",null,null,
|
||||
"https://www.apixu.com/","APIXU"));
|
||||
infopageItemList.add(new infopageItem(R.drawable.somethingnew,
|
||||
"Icon made by: ",
|
||||
"https://www.flaticon.com/authors/hirschwolf",
|
||||
"Hirschwolf",
|
||||
null,
|
||||
null));
|
||||
infopageItemList.add(new infopageItem(R.drawable.breeze,
|
||||
"Icon made by: ",
|
||||
"https://www.flaticon.com/authors/hirschwolf",
|
||||
"Hirchwolf",
|
||||
null,null));
|
||||
infopageItemList.add(new infopageItem(R.drawable.water_drop,
|
||||
"Icon made by: ",
|
||||
"https://www.flaticon.com/authors/freepik",
|
||||
"Freepic",
|
||||
null,null));
|
||||
infopageItemList.add(new infopageItem(R.drawable.cloud_symbol,
|
||||
"Icon made by: ",
|
||||
"https://www.flaticon.com/authors/simpleicon",
|
||||
"Simple Icon",
|
||||
null,null));
|
||||
infopageItemList.add(new infopageItem(R.drawable.sun,
|
||||
"Icon made by: ",
|
||||
"https://www.flaticon.com/authors/freepik",
|
||||
"Freepic",
|
||||
null,null));
|
||||
infopageItemList.add(new infopageItem(R.mipmap.ic_world,
|
||||
"Icon made by: ",
|
||||
"https://www.flaticon.com/authors/freepik",
|
||||
"Freepic",
|
||||
null,null));
|
||||
|
||||
ListView lf = findViewById(R.id.listview_info);
|
||||
lf.setAdapter(new infopageItem.infoPageAdapter(this,infopageItemList));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Address;
|
||||
import android.location.Geocoder;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.dbfiles.ForecastDBHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
|
||||
private SectionsPagerAdapter mSectionsPagerAdapter;
|
||||
private TabLayout tabLayout;
|
||||
private ViewPager mViewPager;
|
||||
public ForecastDBHelper forecastsDbhelper;
|
||||
private String TAG = getClass().getSimpleName();
|
||||
|
||||
public static NetworkInfo networkInfo;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
|
||||
|
||||
mViewPager = (ViewPager) findViewById(R.id.container);
|
||||
mViewPager.setAdapter(mSectionsPagerAdapter);
|
||||
|
||||
tabLayout = (TabLayout) findViewById(R.id.tabs);
|
||||
tabLayout.setupWithViewPager(mViewPager);
|
||||
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
|
||||
|
||||
// iconsInTabs(tabLayout);
|
||||
|
||||
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
|
||||
|
||||
boolean first_time_run = SP.getBoolean("FIRST_TIME_RUN",true);
|
||||
|
||||
Log.i(TAG, "onCreate: notification setup" + first_time_run);
|
||||
|
||||
if (first_time_run) {
|
||||
setupNotificationBroadcaster(getApplicationContext());
|
||||
|
||||
// cal.add(Calendar.SECOND, 5);
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), broadcast);
|
||||
// }
|
||||
}
|
||||
|
||||
iconsInTabs(tabLayout);
|
||||
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
networkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
|
||||
forecastsDbhelper = new ForecastDBHelper(getApplicationContext());
|
||||
|
||||
}
|
||||
|
||||
private void iconsInTabs (TabLayout tabLayout){
|
||||
|
||||
int[] tabIcons = {R.mipmap.ic_home,R.mipmap.ic_world};
|
||||
|
||||
tabLayout.getTabAt(0).setIcon(tabIcons[0]);
|
||||
tabLayout.getTabAt(1).setIcon(tabIcons[1]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Handle action bar item clicks here. The action bar will
|
||||
// automatically handle clicks on the Home/Up button, so long
|
||||
// as you specify a parent activity in AndroidManifest.xml.
|
||||
int id = item.getItemId();
|
||||
|
||||
switch (id){
|
||||
case R.id.action_settings:
|
||||
Intent i = new Intent(this, UnitSettings.class);
|
||||
startActivity(i);
|
||||
return true;
|
||||
case R.id.action_into:
|
||||
Intent infoActivity = new Intent(this,InfoActivity.class);
|
||||
startActivity(infoActivity);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public static String changeToInt(Double d){
|
||||
return String.valueOf(Math.round(d));
|
||||
}
|
||||
|
||||
public static int getImageResource(String s, Context context){
|
||||
|
||||
s =s.replace("cdn.apixu.com/weather/64x64/","");
|
||||
s = s.replace(".png","");
|
||||
s =s.replace("/","_");
|
||||
s = s.substring(2);
|
||||
|
||||
return context.getResources().getIdentifier(s,"drawable",context.getPackageName());
|
||||
}
|
||||
|
||||
public static String getLocationName(Context context, Double latitude, Double longitude) {
|
||||
Geocoder geoCoder = new Geocoder(context, Locale.getDefault());
|
||||
String result = "";
|
||||
List<Address> list = null;
|
||||
try {
|
||||
list = geoCoder.getFromLocation(latitude, longitude, 1);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
if (list != null & list.size() > 0) {
|
||||
Address address = list.get(0);
|
||||
result = address.getLocality();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void setupNotificationBroadcaster(Context context){
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
Intent notificationIntent = new Intent(context, NotificationReceiver.class);
|
||||
PendingIntent broadcast = PendingIntent.getBroadcast(context, 100, notificationIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
|
||||
cal.set(Calendar.HOUR_OF_DAY, 6);
|
||||
cal.set(Calendar.MINUTE, 8);
|
||||
cal.set(Calendar.SECOND, 5);
|
||||
|
||||
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, broadcast);
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder fragment containing a simple view.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
|
||||
* one of the sections/tabs/pages.
|
||||
*/
|
||||
public class SectionsPagerAdapter extends FragmentPagerAdapter {
|
||||
|
||||
public SectionsPagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
switch (position){
|
||||
case 0:
|
||||
Fragment_home tab1 = new Fragment_home();
|
||||
return tab1;
|
||||
case 1:
|
||||
Fragment_Two tab2 = new Fragment_Two();
|
||||
return tab2;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
// Show 2 total pages.
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
// switch (position) {
|
||||
// case 0:
|
||||
// return "Home";
|
||||
// case 1:
|
||||
// return "World";
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.TaskStackBuilder;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Target;
|
||||
import com.squareup.picasso.Transformation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.changeToInt;
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.getImageResource;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.createUrl;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.extractFeatureFromJson;
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.makeHttpRequest;
|
||||
import static com.appttude.h_mal.atlas_weather.getLatLong.latitude;
|
||||
import static com.appttude.h_mal.atlas_weather.getLatLong.longitude;
|
||||
|
||||
/**
|
||||
* Created by h_mal on 29/04/2018.
|
||||
*/
|
||||
|
||||
public class NotificationReceiver extends BroadcastReceiver{
|
||||
|
||||
private String TAG = getClass().getSimpleName();
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "my_notification_channel_1";
|
||||
Context mContext;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
this.mContext = context;
|
||||
|
||||
Log.i(TAG, "onReceive: notif fired");
|
||||
|
||||
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
boolean notif = SP.getBoolean("notif_boolean",true);
|
||||
|
||||
if(notif) {
|
||||
try {
|
||||
getLatLong.configLatLong(mContext);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onReceive: ", e);
|
||||
} finally {
|
||||
if (latitude != null && longitude != null) {
|
||||
String stringURL = RetrieveJSON.UriBuilder(5);
|
||||
NotifAsyncTask task = new NotifAsyncTask();
|
||||
task.execute(stringURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SP.edit().putBoolean("FIRST_TIME_RUN",false).apply();
|
||||
|
||||
}
|
||||
|
||||
private void pushNotif(Context context, ForecastItem forecastItem){
|
||||
Intent notificationIntent = new Intent(context, MainActivity.class);
|
||||
|
||||
CurrentForecast currentForecast = forecastItem.getCurrentForecast();
|
||||
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
||||
stackBuilder.addParentStack(MainActivity.class);
|
||||
stackBuilder.addNextIntent(notificationIntent);
|
||||
|
||||
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
SharedPreferences mSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String temp;
|
||||
if (mSettings.getString("temp_units","").equals("°F")){
|
||||
temp =changeToInt(currentForecast.getTemp_f());
|
||||
}else {
|
||||
temp = changeToInt(currentForecast.getTemp_c());
|
||||
|
||||
}
|
||||
|
||||
Notification.Builder builder = new Notification.Builder(context);
|
||||
Notification notification = builder.setContentTitle("Weather App")
|
||||
.setContentText(temp + "° - " + currentForecast.getCondition_text())
|
||||
.setSmallIcon(R.mipmap.ic_notif) //change icon
|
||||
.setLargeIcon(Icon.createWithResource(context,getImageResource(forecastItem.getCurrentForecast().getIconURL(),context)))
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent).build();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
|
||||
}
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
"NotificationDemo",
|
||||
IMPORTANCE_DEFAULT
|
||||
);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
notificationManager.notify(0, notification);
|
||||
}
|
||||
|
||||
private class NotifAsyncTask extends AsyncTask<String, Void, String> {
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... urlString) {
|
||||
String jsonResponse = null;
|
||||
|
||||
if (urlString.length < 1 || urlString[0] == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
URL url = createUrl(urlString[0]);
|
||||
jsonResponse = makeHttpRequest(url);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Problem making the HTTP request.", e);
|
||||
}
|
||||
|
||||
return jsonResponse;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
super.onPostExecute(result);
|
||||
|
||||
Log.i(TAG, "onPostExecute: " +result);
|
||||
if (result != null && !result.isEmpty()) {
|
||||
final ForecastItem forecastItem = extractFeatureFromJson(result);
|
||||
|
||||
|
||||
pushNotif(mContext,forecastItem);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.changeToInt;
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.getImageResource;
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.getLocationName;
|
||||
|
||||
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private ForecastItem forecastItem;
|
||||
|
||||
private CurrentForecast currentForecast;
|
||||
private List<Forecast> forecast;
|
||||
private SharedPreferences mSettings;
|
||||
private String location;
|
||||
|
||||
|
||||
|
||||
class ViewHolderCurrent extends RecyclerView.ViewHolder {
|
||||
TextView locationTV;
|
||||
TextView conditionTV;
|
||||
ImageView weatherIV;
|
||||
TextView avgTempTV;
|
||||
TextView feels;
|
||||
TextView tempUnit;
|
||||
|
||||
public ViewHolderCurrent(View listItemView) {
|
||||
super(listItemView);
|
||||
locationTV = listItemView.findViewById(R.id.location_main_4);
|
||||
conditionTV = listItemView.findViewById(R.id.condition_main_4);
|
||||
weatherIV = listItemView.findViewById(R.id.icon_main_4);
|
||||
avgTempTV = listItemView.findViewById(R.id.temp_main_4);
|
||||
// feels = listItemView.findViewById(R.id.feelstemp);
|
||||
tempUnit = listItemView.findViewById(R.id.temp_unit_4);
|
||||
}
|
||||
}
|
||||
|
||||
class ViewHolderForecast extends RecyclerView.ViewHolder {
|
||||
|
||||
TextView dateTV;
|
||||
TextView dayTV;
|
||||
TextView conditionTV;
|
||||
ImageView weatherIV;
|
||||
TextView mainTempTV;
|
||||
TextView minorTempTV;
|
||||
|
||||
public ViewHolderForecast(View itemView){
|
||||
super(itemView);
|
||||
dateTV = itemView.findViewById(R.id.list_date);
|
||||
dayTV = itemView.findViewById(R.id.list_day);
|
||||
conditionTV = itemView.findViewById(R.id.list_condition);
|
||||
weatherIV = itemView.findViewById(R.id.list_icon);
|
||||
mainTempTV = itemView.findViewById(R.id.list_main_temp);
|
||||
minorTempTV = itemView.findViewById(R.id.list_minor_temp);
|
||||
}
|
||||
}
|
||||
|
||||
class ViewHolderFurtherDetails extends RecyclerView.ViewHolder {
|
||||
|
||||
TextView windSpeed;
|
||||
TextView windDirection;
|
||||
TextView precipitation;
|
||||
TextView humidity;
|
||||
TextView clouds;
|
||||
|
||||
|
||||
public ViewHolderFurtherDetails(View itemView){
|
||||
super(itemView);
|
||||
windSpeed = itemView.findViewById(R.id.windspeed);
|
||||
windDirection = itemView.findViewById(R.id.winddirection);
|
||||
precipitation = itemView.findViewById(R.id.precip_);
|
||||
humidity = itemView.findViewById(R.id.humidity_);
|
||||
clouds = itemView.findViewById(R.id.clouds_);
|
||||
}
|
||||
}
|
||||
|
||||
public RecyclerViewAdapter(@NonNull Context context, @NonNull ForecastItem forecastItem, Double latitude, Double longitude) {
|
||||
this.context = context;
|
||||
this.forecastItem = forecastItem;
|
||||
this.currentForecast = forecastItem.getCurrentForecast();
|
||||
this.location = getLocationName(context,currentForecast.getLatitude(),currentForecast.getLongitude());
|
||||
|
||||
|
||||
List<Forecast> f = forecastItem.getForecastArrayList();
|
||||
f.remove(0);
|
||||
forecast = f;
|
||||
|
||||
mSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
public RecyclerViewAdapter(@NonNull Context context, @NonNull ForecastItem forecastItem) {
|
||||
this.context = context;
|
||||
this.forecastItem = forecastItem;
|
||||
this.currentForecast = forecastItem.getCurrentForecast();
|
||||
|
||||
List<Forecast> f = forecastItem.getForecastArrayList();
|
||||
f.remove(0);
|
||||
forecast = f;
|
||||
|
||||
mSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
case 1:
|
||||
View viewCurrent = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item4, parent, false);
|
||||
return new ViewHolderCurrent(viewCurrent);
|
||||
|
||||
case 2:
|
||||
View viewForecast = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_layout2, parent, false);
|
||||
return new ViewHolderForecast(viewForecast);
|
||||
|
||||
case 3:
|
||||
View viewFurther = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item3, parent, false);
|
||||
return new ViewHolderFurtherDetails(viewFurther);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
|
||||
|
||||
switch (holder.getItemViewType()) {
|
||||
case 1:
|
||||
final ViewHolderCurrent viewHolderCurrent = (ViewHolderCurrent) holder;
|
||||
|
||||
if (location != null) {
|
||||
viewHolderCurrent.locationTV.setText(location);
|
||||
}else{
|
||||
viewHolderCurrent.locationTV.setText(currentForecast.getLocation());
|
||||
}
|
||||
viewHolderCurrent.conditionTV.setText(currentForecast.getCondition_text());
|
||||
|
||||
viewHolderCurrent.weatherIV.setImageResource(getImageResource(currentForecast.getIconURL(),context));
|
||||
|
||||
if (mSettings.getString("temp_units","").equals("°F")){
|
||||
viewHolderCurrent.avgTempTV.setText(changeToInt(currentForecast.getTemp_f()));
|
||||
// viewHolderCurrent.feels.setText(changeToInt(currentForecast.getFeelslike_f()));
|
||||
viewHolderCurrent.tempUnit.setText("°F");
|
||||
}else {
|
||||
viewHolderCurrent.avgTempTV.setText(changeToInt(currentForecast.getTemp_c()));
|
||||
// viewHolderCurrent.feels.setText(changeToInt(currentForecast.getFeelslike_c()));
|
||||
viewHolderCurrent.tempUnit.setText("°C");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
final ViewHolderForecast viewHolderForecast = (ViewHolderForecast) holder;
|
||||
final Forecast f = forecast.get(position -1);
|
||||
|
||||
Date updatedate = new Date(f.getDate_epoch() * 1000);
|
||||
SimpleDateFormat format = new SimpleDateFormat("EEEE");
|
||||
String day = format.format(updatedate);
|
||||
viewHolderForecast.dayTV.setText(day);
|
||||
format = new SimpleDateFormat("d MMM");
|
||||
String date = format.format(updatedate);
|
||||
viewHolderForecast.dateTV.setText(date);
|
||||
|
||||
if(f.getForecast_text().equals("Moderate or heavy rain shower")){
|
||||
viewHolderForecast.conditionTV.setText("Moderate/Heavy Showers");
|
||||
}else{
|
||||
viewHolderForecast.conditionTV.setText(f.getForecast_text());
|
||||
}
|
||||
|
||||
viewHolderForecast.weatherIV.setImageResource(getImageResource(f.getIconURL(), context));
|
||||
|
||||
if (mSettings.getString("temp_units","").equals("°F")){
|
||||
viewHolderForecast.mainTempTV.setText(changeToInt(f.getMaxtemp_f()));
|
||||
viewHolderForecast.minorTempTV.setText(changeToInt(f.getMintemp_f()));
|
||||
}else {
|
||||
viewHolderForecast.mainTempTV.setText(changeToInt(f.getMaxtemp_c()));
|
||||
viewHolderForecast.minorTempTV.setText(changeToInt(f.getMintemp_c()));
|
||||
}
|
||||
|
||||
viewHolderForecast.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
openFurtherInfo(f);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
final ViewHolderFurtherDetails viewHolderFurtherDetails = (ViewHolderFurtherDetails) holder;
|
||||
|
||||
|
||||
if (mSettings.getString("wind_units","").equals("mph")) {
|
||||
viewHolderFurtherDetails.windSpeed.setText(new StringBuilder().append(changeToInt(currentForecast.getWind_mph()))
|
||||
.append(context.getResources().getStringArray(R.array.list_preference_wind_values)[0]));
|
||||
}else{
|
||||
viewHolderFurtherDetails.windSpeed.setText(new StringBuilder().append(changeToInt(currentForecast.getWind_kph()))
|
||||
.append(context.getResources().getStringArray(R.array.list_preference_wind_values)[1]).toString());
|
||||
|
||||
}
|
||||
viewHolderFurtherDetails.windDirection.setText(currentForecast.getWind_dir());
|
||||
viewHolderFurtherDetails.humidity.setText(new StringBuilder().append(changeToInt(currentForecast.getHumidity()))
|
||||
.append("%").toString());
|
||||
|
||||
if (mSettings.getString("precip_units","").equals("in")) {
|
||||
viewHolderFurtherDetails.precipitation.setText(new StringBuilder().append(changeToInt(currentForecast.getPrecip_mm()))
|
||||
.append(context.getResources().getStringArray(R.array.list_preference_precip_values)[1]));
|
||||
}else{
|
||||
viewHolderFurtherDetails.precipitation.setText(new StringBuilder().append(changeToInt(currentForecast.getPrecip_mm()))
|
||||
.append(context.getResources().getStringArray(R.array.list_preference_precip_values)[0]));
|
||||
}
|
||||
viewHolderFurtherDetails.clouds.setText(changeToInt(currentForecast.getCloud()) + "%");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return forecastItem.getForecastArrayList().size() + 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
int type = 0;
|
||||
if (position == 0){
|
||||
type = 1;
|
||||
}else if (position >= 1 && position < getItemCount() -1){
|
||||
type = 2;
|
||||
}else if (position == getItemCount() -1){
|
||||
type = 3;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private void openFurtherInfo(Forecast forcast){
|
||||
Intent i = new Intent(context,FurtherInfoActivity.class);
|
||||
i.putExtra("currentForcast",forcast);
|
||||
context.startActivity(i);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.Fragment_home.APIkey;
|
||||
import static com.appttude.h_mal.atlas_weather.getLatLong.latitude;
|
||||
import static com.appttude.h_mal.atlas_weather.getLatLong.longitude;
|
||||
|
||||
/**
|
||||
* Created by h_mal on 05/05/2018.
|
||||
*/
|
||||
|
||||
public class RetrieveJSON {
|
||||
|
||||
public RetrieveJSON(){}
|
||||
|
||||
public static String UriBuilder(int days){
|
||||
|
||||
String latLong = latitude + "," + longitude;
|
||||
|
||||
Uri.Builder builder = new Uri.Builder();
|
||||
builder.scheme("http")
|
||||
.authority("api.apixu.com")
|
||||
.appendPath("v1")
|
||||
.appendPath("forecast.json")
|
||||
.appendQueryParameter("key",APIkey)
|
||||
.appendQueryParameter("q",latLong)
|
||||
.appendQueryParameter("days",days+"");
|
||||
|
||||
return builder.build().toString().replace("%2C",",");
|
||||
|
||||
}
|
||||
|
||||
protected static String UriBuilder(String q){
|
||||
|
||||
Uri.Builder builder = new Uri.Builder();
|
||||
builder.scheme("http")
|
||||
.authority("api.apixu.com")
|
||||
.appendPath("v1")
|
||||
.appendPath("forecast.json")
|
||||
.appendQueryParameter("key",APIkey)
|
||||
.appendQueryParameter("q",q)
|
||||
.appendQueryParameter("days","7");
|
||||
|
||||
return builder.build().toString().replace("%2C",",");
|
||||
|
||||
}
|
||||
|
||||
public static URL createUrl(String stringUrl) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = new URL(stringUrl);
|
||||
} catch (MalformedURLException e) {
|
||||
Log.e("ERROR", "Error with creating URL ", e);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public static String makeHttpRequest(URL url) throws IOException {
|
||||
String jsonResponse = null;
|
||||
|
||||
if (url == null) {
|
||||
return jsonResponse;
|
||||
}
|
||||
|
||||
HttpURLConnection urlConnection = null;
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setReadTimeout(30000);
|
||||
urlConnection.setConnectTimeout(30000);
|
||||
urlConnection.setRequestMethod("GET");
|
||||
urlConnection.connect();
|
||||
|
||||
if (urlConnection.getResponseCode() == 200) {
|
||||
inputStream = urlConnection.getInputStream();
|
||||
jsonResponse = readFromStream(inputStream);
|
||||
} else {
|
||||
Log.e("", "Error response code: " + urlConnection.getResponseCode());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("", "Problem retrieving the JSON results.", e);
|
||||
} finally {
|
||||
if (urlConnection != null) {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
return jsonResponse;
|
||||
}
|
||||
|
||||
private static String readFromStream(InputStream inputStream) throws IOException {
|
||||
StringBuilder output = new StringBuilder();
|
||||
if (inputStream != null) {
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
|
||||
BufferedReader reader = new BufferedReader(inputStreamReader);
|
||||
String line = "";
|
||||
while (line != null) {
|
||||
output.append(line);
|
||||
line = reader.readLine();
|
||||
}
|
||||
}
|
||||
Log.d("", output.toString());
|
||||
return output.toString();
|
||||
|
||||
}
|
||||
|
||||
public static ForecastItem extractFeatureFromJson(String newsJSON) {
|
||||
|
||||
if (TextUtils.isEmpty(newsJSON)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CurrentForecast forecastCurrent = new CurrentForecast();
|
||||
List<Forecast> forecasts = new ArrayList<Forecast>();
|
||||
|
||||
try {
|
||||
JSONObject baseJsonResponse = new JSONObject(newsJSON);
|
||||
JSONObject locationObject = baseJsonResponse.getJSONObject("location");
|
||||
Double latitude = locationObject.getDouble("lat");
|
||||
Double longitude = locationObject.getDouble("lon");
|
||||
String location = locationObject.getString("name");
|
||||
int last_updated_epoch = locationObject.getInt("localtime_epoch");
|
||||
|
||||
JSONObject currentObject = baseJsonResponse.getJSONObject("current");
|
||||
Double temp_c = currentObject.getDouble("temp_c");
|
||||
Double temp_f = currentObject.getDouble("temp_f");
|
||||
|
||||
JSONObject conditionObject = currentObject.getJSONObject("condition");
|
||||
String condition_text = conditionObject.getString("text");
|
||||
String iconURL = conditionObject.getString("icon");
|
||||
|
||||
Double wind_mph = currentObject.getDouble("wind_mph");
|
||||
Double wind_kph = currentObject.getDouble("wind_kph");
|
||||
String wind_dir = currentObject.getString("wind_dir");
|
||||
Double pressure_mb = currentObject.getDouble("pressure_mb");
|
||||
Double pressure_in = currentObject.getDouble("pressure_in");
|
||||
Double precip_mm = currentObject.getDouble("precip_mm");
|
||||
Double precip_in = currentObject.getDouble("precip_in");
|
||||
Double humidity = currentObject.getDouble("humidity");
|
||||
Double cloud = currentObject.getDouble("cloud");
|
||||
Double feelslike_c = currentObject.getDouble("feelslike_c");
|
||||
Double feelslike_f = currentObject.getDouble("feelslike_f");
|
||||
Double vis_km = currentObject.getDouble("vis_km");
|
||||
Double vis_miles = currentObject.getDouble("vis_miles");
|
||||
|
||||
forecastCurrent = new CurrentForecast(location, latitude, longitude,
|
||||
last_updated_epoch, temp_c, temp_f, condition_text,
|
||||
iconURL, wind_mph, wind_kph, wind_dir,
|
||||
pressure_mb, pressure_in, precip_mm, precip_in,
|
||||
humidity, cloud, feelslike_c, feelslike_f,
|
||||
vis_km, vis_miles
|
||||
);
|
||||
|
||||
JSONObject forecastObject = baseJsonResponse.getJSONObject("forecast");
|
||||
JSONArray forecastsArray = forecastObject.getJSONArray("forecastday");
|
||||
|
||||
for (int i = 0; i < forecastsArray.length(); i++) {
|
||||
JSONObject currentForecastObject = forecastsArray.getJSONObject(i);
|
||||
Long date = currentForecastObject.getLong("date_epoch");
|
||||
|
||||
JSONObject day = currentForecastObject.getJSONObject("day");
|
||||
Double maxtemp_c = day.getDouble ("maxtemp_c");
|
||||
Double maxtemp_f = day.getDouble ("maxtemp_f");
|
||||
Double mintemp_c = day.getDouble ("mintemp_c");
|
||||
Double mintemp_f = day.getDouble ("mintemp_f");
|
||||
Double avgtemp_c = day.getDouble ("avgtemp_c");
|
||||
Double avgtemp_f = day.getDouble ("avgtemp_f");
|
||||
Double maxwind_mph = day.getDouble ("maxwind_mph");
|
||||
Double maxwind_kph = day.getDouble ("maxwind_kph");
|
||||
Double totalprecip_mm = day.getDouble ("totalprecip_mm");
|
||||
Double totalprecip_in = day.getDouble ("totalprecip_in");
|
||||
Double avgvis_km = day.getDouble ("avgvis_km");
|
||||
Double avgvis_miles = day.getDouble ("avgvis_miles");
|
||||
Double avghumidity = day.getDouble ("avghumidity");
|
||||
Double uv = day.getDouble("uv");
|
||||
|
||||
JSONObject condition = day.getJSONObject("condition");
|
||||
String conditionText = condition.getString("text");
|
||||
String imageURL = condition.getString("icon");
|
||||
|
||||
JSONObject astro = currentForecastObject.getJSONObject("astro");
|
||||
String sunrise = astro.getString("sunrise");
|
||||
String sunset = astro.getString("sunset");
|
||||
String moonrise = astro.getString("moonrise");
|
||||
String moonset = astro.getString("moonset");
|
||||
|
||||
Forecast forecast = new Forecast(date, maxtemp_c, maxtemp_f, mintemp_c, mintemp_f, avgtemp_c, avgtemp_f,
|
||||
maxwind_mph, maxwind_kph, totalprecip_mm, totalprecip_in, avgvis_km, avgvis_miles,
|
||||
avghumidity, conditionText, imageURL, uv, sunrise, sunset, moonrise, moonset)
|
||||
;
|
||||
|
||||
forecasts.add(forecast);
|
||||
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e("Error", "Problem parsing the book JSON results", e);
|
||||
}
|
||||
|
||||
return new ForecastItem(forecastCurrent,forecasts);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.AppWidget.NewAppWidget;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.MainActivity.setupNotificationBroadcaster;
|
||||
|
||||
public class UnitSettings extends PreferenceActivity {
|
||||
|
||||
private String TAG = getClass().getSimpleName();
|
||||
private SharedPreferences.OnSharedPreferenceChangeListener prefListener;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
PreferenceManager.setDefaultValues(this, R.xml.prefs, false);
|
||||
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
|
||||
|
||||
//listener on changed sort order preference:
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
|
||||
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
||||
|
||||
if(key.equals("temp_units")){
|
||||
Intent intent = new Intent(getBaseContext(), NewAppWidget.class);
|
||||
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
|
||||
int[] ids = AppWidgetManager.getInstance(getApplication()).getAppWidgetIds(new ComponentName(getApplication(), NewAppWidget.class));
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
|
||||
if(key.equals("notif_boolean")){
|
||||
setupNotificationBroadcaster(getBaseContext());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
prefs.registerOnSharedPreferenceChangeListener(prefListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
|
||||
// Log.i(TAG, "onSharedPreferenceChanged: " + s);
|
||||
// if (s == "temp_units"){
|
||||
// Intent intent = new Intent(getBaseContext(), NewAppWidget.class);
|
||||
// intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
//
|
||||
// int[] ids = AppWidgetManager.getInstance(getApplication()).getAppWidgetIds(new ComponentName(getApplication(), NewAppWidget.class));
|
||||
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
|
||||
// sendBroadcast(intent);
|
||||
// }
|
||||
// }
|
||||
|
||||
public static class MyPreferenceFragment extends PreferenceFragment
|
||||
{
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.prefs);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.appttude.h_mal.atlas_weather.RetrieveJSON.UriBuilder;
|
||||
|
||||
public class WorldItemActivity extends AppCompatActivity
|
||||
implements android.support.v4.app.LoaderManager.LoaderCallbacks<List<ForecastItem>> {
|
||||
|
||||
private static final int NEWS_LOADER_ID = 1;
|
||||
|
||||
RecyclerView forecastsListView;
|
||||
LinearLayout emptyList;
|
||||
ProgressBar mainPG;
|
||||
SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
private RecyclerViewAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.fragment_main);
|
||||
|
||||
Intent mIntent = getIntent();
|
||||
ForecastItem forecast = mIntent.getParcelableExtra("ForecastItem");
|
||||
|
||||
forecastsListView = findViewById(R.id.forecast_listview);
|
||||
forecastsListView.setLayoutManager(new LinearLayoutManager(this));
|
||||
emptyList = findViewById(R.id.emptyView);
|
||||
mainPG = findViewById(R.id.mainPB);
|
||||
|
||||
emptyList.setVisibility(View.GONE);
|
||||
mainPG.setVisibility(View.GONE);
|
||||
|
||||
mAdapter = new RecyclerViewAdapter(this,forecast);
|
||||
forecastsListView.setAdapter(mAdapter);
|
||||
|
||||
mSwipeRefreshLayout = findViewById(R.id.swipe_refresh);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
|
||||
// if (networkInfo != null && networkInfo.isConnected()) {
|
||||
// android.support.v4.app.LoaderManager loaderManager = getSupportLoaderManager();
|
||||
// loaderManager.initLoader(NEWS_LOADER_ID, null, this);
|
||||
// } else {
|
||||
// emptyList.setVisibility(View.VISIBLE);
|
||||
// mainPG.setVisibility(View.GONE);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.support.v4.content.Loader<List<ForecastItem>> onCreateLoader(int id, Bundle args) {
|
||||
// URL url = createUrl(UriBuilder());
|
||||
// ArrayList<String> entries = new ArrayList<>();
|
||||
// entries.add(url.toString());
|
||||
// return new ForecastLoader(this,entries);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(android.support.v4.content.Loader<List<ForecastItem>> loader, List<ForecastItem> data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(android.support.v4.content.Loader<List<ForecastItem>> loader) {
|
||||
// loader.reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.appttude.h_mal.atlas_weather.application
|
||||
|
||||
import android.app.Application
|
||||
import androidx.test.espresso.idling.CountingIdlingResource
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
|
||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
|
||||
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
|
||||
import com.appttude.h_mal.atlas_weather.notification.ServicesHelper
|
||||
import com.google.gson.Gson
|
||||
import org.kodein.di.Kodein
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.x.androidXModule
|
||||
import org.kodein.di.generic.bind
|
||||
import org.kodein.di.generic.instance
|
||||
import org.kodein.di.generic.provider
|
||||
import org.kodein.di.generic.singleton
|
||||
|
||||
const val LOCATION_PERMISSION_REQUEST = 505
|
||||
class AppClass : Application(), KodeinAware {
|
||||
|
||||
companion object{
|
||||
// idling resource to be used for espresso testing
|
||||
// when we need to wait for async operations to complete
|
||||
val idlingResources = CountingIdlingResource("Data_loader")
|
||||
}
|
||||
|
||||
// Kodein creation of modules to be retrieve within the app
|
||||
override val kodein = Kodein.lazy {
|
||||
import(androidXModule(this@AppClass))
|
||||
|
||||
bind() from singleton { Gson() }
|
||||
|
||||
bind() from singleton { NetworkConnectionInterceptor(instance()) }
|
||||
bind() from singleton { QueryParamsInterceptor() }
|
||||
bind() from singleton { WeatherApi(instance(), instance())}
|
||||
bind() from singleton { AppDatabase(instance()) }
|
||||
bind() from singleton { PreferenceProvider(instance()) }
|
||||
bind() from singleton { RepositoryImpl(instance(), instance(), instance()) }
|
||||
bind() from singleton { SettingsRepositoryImpl(instance()) }
|
||||
bind() from singleton { LocationProvider(instance()) }
|
||||
bind() from singleton { ServicesHelper(instance(), instance(), instance()) }
|
||||
bind() from provider { ApplicationViewModelFactory(instance(), instance()) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.location
|
||||
|
||||
import android.Manifest.permission.ACCESS_FINE_LOCATION
|
||||
import android.content.Context
|
||||
import android.content.Context.LOCATION_SERVICE
|
||||
import android.location.Geocoder
|
||||
import android.location.LocationManager
|
||||
import androidx.annotation.RequiresPermission
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
|
||||
class LocationProvider(
|
||||
val applicationContext: Context
|
||||
) {
|
||||
|
||||
|
||||
private var locationManager =
|
||||
applicationContext.getSystemService(LOCATION_SERVICE) as LocationManager?
|
||||
|
||||
private val geoCoder: Geocoder by lazy { Geocoder(applicationContext, Locale.getDefault()) }
|
||||
|
||||
@RequiresPermission(value = ACCESS_FINE_LOCATION)
|
||||
fun getLatLong(): Pair<Double, Double>{
|
||||
val location = locationManager
|
||||
?.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
||||
?: locationManager?.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
|
||||
location ?: throw IOException("Unable to get location")
|
||||
|
||||
val lat = location.latitude
|
||||
val long = location.longitude
|
||||
return Pair(lat, long)
|
||||
}
|
||||
|
||||
fun getLocationName(lat: Double, long: Double): String{
|
||||
val result = geoCoder.getFromLocation(lat, long, 1)?.get(0)
|
||||
|
||||
return result?.let { location ->
|
||||
location.locality?.let { return it }
|
||||
location.thoroughfare?.let { return it }
|
||||
location.subAdminArea?.let { return it }
|
||||
} ?: "$lat, $long"
|
||||
}
|
||||
|
||||
fun getLatLongFromLocationString(location: String): Pair<Double, Double>{
|
||||
val locations = geoCoder.getFromLocationName(location, 1)
|
||||
|
||||
locations?.takeIf { it.isNotEmpty() }?.get(0)?.let {
|
||||
return Pair(it.latitude, it.longitude)
|
||||
}
|
||||
|
||||
throw IOException("No location found")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import retrofit2.Response
|
||||
import java.io.IOException
|
||||
|
||||
abstract class ResponseUnwrap {
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
suspend fun <T : Any> responseUnwrap(
|
||||
call: suspend () -> Response<T>
|
||||
): T {
|
||||
|
||||
val response = call.invoke()
|
||||
if (response.isSuccessful) {
|
||||
return response.body()!!
|
||||
} else {
|
||||
val error = response.errorBody()?.string()
|
||||
|
||||
val errorMessage = error?.let {
|
||||
try {
|
||||
JSONObject(it).getString("message")
|
||||
} catch (e: JSONException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
} ?: "Error Code: ${response.code()}"
|
||||
|
||||
throw IOException(errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
|
||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Response
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
||||
interface WeatherApi {
|
||||
|
||||
@GET("onecall?")
|
||||
suspend fun getFromApi(
|
||||
@Query("lat") query: String,
|
||||
@Query("lon") lon: String,
|
||||
@Query("exclude") exclude: String = "hourly,minutely",
|
||||
@Query("units") units: String = "metric"
|
||||
): Response<WeatherResponse>
|
||||
|
||||
// invoke method creating an invocation of the api call
|
||||
companion object{
|
||||
operator fun invoke(
|
||||
// injected @params
|
||||
networkConnectionInterceptor: NetworkConnectionInterceptor,
|
||||
queryParamsInterceptor: QueryParamsInterceptor
|
||||
) : WeatherApi {
|
||||
|
||||
|
||||
val baseUrl = "https://api.openweathermap.org/data/2.5/"
|
||||
|
||||
// okHttpClient with interceptors
|
||||
val okkHttpclient = OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(networkConnectionInterceptor)
|
||||
.addInterceptor(queryParamsInterceptor)
|
||||
.build()
|
||||
|
||||
// creation of retrofit class
|
||||
return Retrofit.Builder()
|
||||
.client(okkHttpclient)
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
.create(WeatherApi::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.interceptors
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import okhttp3.Interceptor
|
||||
import java.io.IOException
|
||||
|
||||
class NetworkConnectionInterceptor(
|
||||
context: Context
|
||||
) : Interceptor {
|
||||
|
||||
private val applicationContext = context.applicationContext
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
|
||||
if (!isInternetAvailable()){
|
||||
throw IOException("Make sure you have an active data connection")
|
||||
}
|
||||
return chain.proceed(chain.request())
|
||||
}
|
||||
|
||||
private fun isInternetAvailable(): Boolean {
|
||||
var result = false
|
||||
val connectivityManager =
|
||||
applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
||||
connectivityManager?.let {
|
||||
it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply {
|
||||
result = when {
|
||||
hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.interceptors
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.BuildConfig
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
||||
/**
|
||||
* Interceptor used to add default query parameters to api calls
|
||||
*/
|
||||
class QueryParamsInterceptor : Interceptor{
|
||||
|
||||
val id = BuildConfig.ParamOne
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val original = chain.request()
|
||||
val originalHttpUrl = original.url()
|
||||
|
||||
val url = originalHttpUrl.newBuilder()
|
||||
.addQueryParameter("appid", id)
|
||||
.build()
|
||||
|
||||
// Request customization: add request headers
|
||||
// Request customization: add request headers
|
||||
val requestBuilder: Request.Builder = original.newBuilder()
|
||||
.url(url)
|
||||
|
||||
val request: Request = requestBuilder.build()
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.response.forecast
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Current(
|
||||
|
||||
@field:SerializedName("sunrise")
|
||||
val sunrise: Int? = null,
|
||||
|
||||
@field:SerializedName("temp")
|
||||
val temp: Double? = null,
|
||||
|
||||
@field:SerializedName("visibility")
|
||||
val visibility: Int? = null,
|
||||
|
||||
@field:SerializedName("uvi")
|
||||
val uvi: Double? = null,
|
||||
|
||||
@field:SerializedName("pressure")
|
||||
val pressure: Int? = null,
|
||||
|
||||
@field:SerializedName("clouds")
|
||||
val clouds: Int? = null,
|
||||
|
||||
@field:SerializedName("feels_like")
|
||||
val feelsLike: Double? = null,
|
||||
|
||||
@field:SerializedName("dt")
|
||||
val dt: Int? = null,
|
||||
|
||||
@field:SerializedName("wind_deg")
|
||||
val windDeg: Int? = null,
|
||||
|
||||
@field:SerializedName("dew_point")
|
||||
val dewPoint: Double? = null,
|
||||
|
||||
@field:SerializedName("sunset")
|
||||
val sunset: Int? = null,
|
||||
|
||||
@field:SerializedName("weather")
|
||||
val weather: List<WeatherItem?>? = null,
|
||||
|
||||
@field:SerializedName("humidity")
|
||||
val humidity: Int? = null,
|
||||
|
||||
@field:SerializedName("wind_speed")
|
||||
val windSpeed: Double? = null
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.response.forecast
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DailyItem(
|
||||
|
||||
@field:SerializedName("sunrise")
|
||||
val sunrise: Int? = null,
|
||||
|
||||
@field:SerializedName("temp")
|
||||
val temp: Temp? = null,
|
||||
|
||||
@field:SerializedName("uvi")
|
||||
val uvi: Double? = null,
|
||||
|
||||
@field:SerializedName("pressure")
|
||||
val pressure: Int? = null,
|
||||
|
||||
@field:SerializedName("clouds")
|
||||
val clouds: Int? = null,
|
||||
|
||||
@field:SerializedName("feels_like")
|
||||
val feelsLike: FeelsLike? = null,
|
||||
|
||||
@field:SerializedName("dt")
|
||||
val dt: Int? = null,
|
||||
|
||||
@field:SerializedName("pop")
|
||||
val pop: Double? = null,
|
||||
|
||||
@field:SerializedName("wind_deg")
|
||||
val windDeg: Int? = null,
|
||||
|
||||
@field:SerializedName("dew_point")
|
||||
val dewPoint: Double? = null,
|
||||
|
||||
@field:SerializedName("sunset")
|
||||
val sunset: Int? = null,
|
||||
|
||||
@field:SerializedName("weather")
|
||||
val weather: List<WeatherItem?>? = null,
|
||||
|
||||
@field:SerializedName("humidity")
|
||||
val humidity: Int? = null,
|
||||
|
||||
@field:SerializedName("wind_speed")
|
||||
val windSpeed: Double? = null,
|
||||
|
||||
@field:SerializedName("rain")
|
||||
val rain: Double? = null
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.response.forecast
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FeelsLike(
|
||||
|
||||
@field:SerializedName("eve")
|
||||
val eve: Double? = null,
|
||||
|
||||
@field:SerializedName("night")
|
||||
val night: Double? = null,
|
||||
|
||||
@field:SerializedName("day")
|
||||
val day: Double? = null,
|
||||
|
||||
@field:SerializedName("morn")
|
||||
val morn: Double? = null
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.response.forecast
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Response(
|
||||
|
||||
@field:SerializedName("current")
|
||||
val current: Current? = null,
|
||||
|
||||
@field:SerializedName("timezone")
|
||||
val timezone: String? = null,
|
||||
|
||||
@field:SerializedName("timezone_offset")
|
||||
val timezoneOffset: Int? = null,
|
||||
|
||||
@field:SerializedName("daily")
|
||||
val daily: List<DailyItem?>? = null,
|
||||
|
||||
@field:SerializedName("lon")
|
||||
val lon: Double? = null,
|
||||
|
||||
@field:SerializedName("lat")
|
||||
val lat: Double? = null
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.response.forecast
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Temp(
|
||||
|
||||
@field:SerializedName("min")
|
||||
val min: Double? = null,
|
||||
|
||||
@field:SerializedName("max")
|
||||
val max: Double? = null,
|
||||
|
||||
@field:SerializedName("eve")
|
||||
val eve: Double? = null,
|
||||
|
||||
@field:SerializedName("night")
|
||||
val night: Double? = null,
|
||||
|
||||
@field:SerializedName("day")
|
||||
val day: Double? = null,
|
||||
|
||||
@field:SerializedName("morn")
|
||||
val morn: Double? = null
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.response.forecast
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class WeatherItem(
|
||||
|
||||
@field:SerializedName("icon")
|
||||
val icon: String? = null,
|
||||
|
||||
@field:SerializedName("description")
|
||||
val description: String? = null,
|
||||
|
||||
@field:SerializedName("main")
|
||||
val main: String? = null,
|
||||
|
||||
@field:SerializedName("id")
|
||||
val id: Int? = null
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.response.forecast
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class WeatherResponse(
|
||||
|
||||
@field:SerializedName("current")
|
||||
val current: Current? = null,
|
||||
|
||||
@field:SerializedName("timezone")
|
||||
val timezone: String? = null,
|
||||
|
||||
@field:SerializedName("timezone_offset")
|
||||
val timezoneOffset: Int? = null,
|
||||
|
||||
@field:SerializedName("daily")
|
||||
val daily: List<DailyItem>? = null,
|
||||
|
||||
@field:SerializedName("lon")
|
||||
val lon: Double = 0.00,
|
||||
|
||||
@field:SerializedName("lat")
|
||||
val lat: Double = 0.00
|
||||
)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.prefs
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
||||
|
||||
/**
|
||||
* Shared preferences to save & load last timestamp
|
||||
*/
|
||||
const val LOCATION_CONST = "location_"
|
||||
class PreferenceProvider(
|
||||
context: Context
|
||||
){
|
||||
|
||||
private val appContext = context.applicationContext
|
||||
|
||||
private val preference: SharedPreferences
|
||||
get() = PreferenceManager.getDefaultSharedPreferences(appContext)
|
||||
|
||||
fun saveLastSavedAt(locationName: String) {
|
||||
preference.edit().putLong(
|
||||
locationName,
|
||||
System.currentTimeMillis()
|
||||
).apply()
|
||||
}
|
||||
|
||||
fun getLastSavedAt(locationName: String): Long? {
|
||||
return preference.getLong(locationName, 0L)
|
||||
}
|
||||
|
||||
fun getAllKeys() = preference.all.keys.apply {
|
||||
remove(CURRENT_LOCATION)
|
||||
}
|
||||
|
||||
fun deleteLocation(locationName: String){
|
||||
preference.edit().remove(locationName).apply()
|
||||
}
|
||||
|
||||
fun isNotificationsEnabled(): Boolean = preference.getBoolean("notif_boolean", true)
|
||||
|
||||
fun setFirstTimeRun(){
|
||||
preference.edit().putBoolean("FIRST_TIME_RUN", false).apply()
|
||||
}
|
||||
|
||||
fun isWidgetBlackground(): Boolean {
|
||||
return preference.getBoolean("widget_black_background", false)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.repository
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||
|
||||
interface Repository {
|
||||
|
||||
suspend fun getWeatherFromApi(lat: String, long: String): WeatherResponse
|
||||
suspend fun saveCurrentWeatherToRoom(locationId: String, weatherResponse: WeatherResponse)
|
||||
suspend fun saveWeatherListToRoom(list: List<EntityItem>)
|
||||
fun loadAllWeatherExceptCurrentFromRoom(): LiveData<List<EntityItem>>
|
||||
fun loadCurrentWeatherFromRoom(id: String): LiveData<EntityItem>
|
||||
suspend fun loadSingleCurrentWeatherFromRoom(id: String): EntityItem
|
||||
fun isSearchValid(locationName: String): Boolean
|
||||
fun saveLastSavedAt(locationName: String)
|
||||
suspend fun deleteSavedWeatherEntry(locationName: String)
|
||||
fun getSavedLocations(): List<String>
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.repository
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.ResponseUnwrap
|
||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.LOCATION_CONST
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
||||
|
||||
private const val FIVE_MINS = 300000L
|
||||
class RepositoryImpl(
|
||||
private val api: WeatherApi,
|
||||
private val db: AppDatabase,
|
||||
private val prefs: PreferenceProvider
|
||||
) : Repository, ResponseUnwrap() {
|
||||
|
||||
override suspend fun getWeatherFromApi(
|
||||
lat: String,
|
||||
long: String
|
||||
): WeatherResponse {
|
||||
return responseUnwrap { api.getFromApi(lat, long) }
|
||||
}
|
||||
|
||||
override suspend fun saveCurrentWeatherToRoom(
|
||||
locationId: String,
|
||||
weatherResponse: WeatherResponse
|
||||
){
|
||||
val entity = EntityItem(
|
||||
locationId,
|
||||
FullWeather(weatherResponse)
|
||||
)
|
||||
db.getSimpleDao().upsertFullWeather(entity)
|
||||
}
|
||||
|
||||
override suspend fun saveWeatherListToRoom(
|
||||
list: List<EntityItem>
|
||||
){
|
||||
db.getSimpleDao().upsertListOfFullWeather(list)
|
||||
}
|
||||
|
||||
override fun loadAllWeatherExceptCurrentFromRoom() = db.getSimpleDao().getAllFullWeatherWithoutCurrent()
|
||||
|
||||
override fun loadCurrentWeatherFromRoom(id: String) = db.getSimpleDao().getCurrentFullWeather(id)
|
||||
|
||||
override suspend fun loadSingleCurrentWeatherFromRoom(id: String) = db.getSimpleDao().getCurrentFullWeatherSingle(id)
|
||||
|
||||
override fun isSearchValid(locationName: String): Boolean {
|
||||
val lastSaved = prefs.getLastSavedAt(locationName) ?: return true
|
||||
val difference = System.currentTimeMillis() - lastSaved
|
||||
return difference > FIVE_MINS
|
||||
}
|
||||
|
||||
override fun saveLastSavedAt(locationName: String) {
|
||||
prefs.saveLastSavedAt("$LOCATION_CONST$locationName")
|
||||
}
|
||||
|
||||
override suspend fun deleteSavedWeatherEntry(locationName: String){
|
||||
db.getSimpleDao().deleteEntry(locationName)
|
||||
prefs.deleteLocation(locationName)
|
||||
}
|
||||
|
||||
override fun getSavedLocations(): List<String> {
|
||||
return prefs.getAllKeys().toList()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.repository
|
||||
|
||||
interface SettingsRepository {
|
||||
fun isNotificationsEnabled(): Boolean
|
||||
fun setFirstTime()
|
||||
fun isBlackBackground(): Boolean
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.repository
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||
|
||||
class SettingsRepositoryImpl(
|
||||
val prefs: PreferenceProvider
|
||||
) : SettingsRepository{
|
||||
|
||||
override fun isNotificationsEnabled(): Boolean = prefs.isNotificationsEnabled()
|
||||
|
||||
override fun setFirstTime() = prefs.setFirstTimeRun()
|
||||
|
||||
override fun isBlackBackground() = prefs.isWidgetBlackground()
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.room
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||
|
||||
@Database(
|
||||
entities = [EntityItem::class],
|
||||
version = 1,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(Converter::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun getSimpleDao(): WeatherDao
|
||||
|
||||
companion object {
|
||||
|
||||
@Volatile
|
||||
private var instance: AppDatabase? = null
|
||||
private val LOCK = Any()
|
||||
|
||||
// create an instance of room database or use previously created instance
|
||||
operator fun invoke(context: Context) = instance ?: synchronized(LOCK) {
|
||||
instance ?: buildDatabase(context).also {
|
||||
instance = it
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDatabase(context: Context) =
|
||||
Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
AppDatabase::class.java,
|
||||
"MyDatabase.db"
|
||||
).addTypeConverter(Converter(context))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.room
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.ProvidedTypeConverter
|
||||
import androidx.room.TypeConverter
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
||||
import com.google.gson.Gson
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.kodein
|
||||
import org.kodein.di.generic.instance
|
||||
|
||||
@ProvidedTypeConverter
|
||||
class Converter(context: Context): KodeinAware{
|
||||
override val kodein by kodein(context)
|
||||
private val gson by instance<Gson>()
|
||||
|
||||
@TypeConverter
|
||||
fun fullWeatherToString(fullWeather: FullWeather): String{
|
||||
return gson.toJson(fullWeather)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun stringToFullWeather(string: String): FullWeather{
|
||||
return gson.fromJson(string, FullWeather::class.java)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.room
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||
|
||||
@Dao
|
||||
interface WeatherDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertFullWeather(item: EntityItem)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertListOfFullWeather(items: List<EntityItem>)
|
||||
|
||||
@Query("SELECT * FROM EntityItem WHERE id = :userId LIMIT 1")
|
||||
fun getCurrentFullWeather(userId: String) : LiveData<EntityItem>
|
||||
|
||||
@Query("SELECT * FROM EntityItem WHERE id = :userId LIMIT 1")
|
||||
suspend fun getCurrentFullWeatherSingle(userId: String) : EntityItem
|
||||
|
||||
@Query("SELECT * FROM EntityItem WHERE id != :id")
|
||||
fun getAllFullWeatherWithoutCurrent(id: String = CURRENT_LOCATION) : LiveData<List<EntityItem>>
|
||||
|
||||
@Query("DELETE FROM EntityItem WHERE id = :userId")
|
||||
suspend fun deleteEntry(userId: String)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.room.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
||||
|
||||
|
||||
const val CURRENT_LOCATION = "CurrentLocation"
|
||||
@Entity
|
||||
data class EntityItem(
|
||||
@PrimaryKey(autoGenerate = false)
|
||||
val id: String,
|
||||
val weather: FullWeather
|
||||
)
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather.dbfiles;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
public class ForecastContract {
|
||||
|
||||
public ForecastContract() {
|
||||
}
|
||||
|
||||
public static final String CONTENT_AUTHORITY = "com.appttude.h_mal.atlas_weather";
|
||||
|
||||
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
|
||||
|
||||
public static final String PATH_FORECASTS = "forecasts";
|
||||
public static final String PATH_FORECASTS_WIDGET = "widgetitems";
|
||||
|
||||
public static final class ForecastEntry implements BaseColumns {
|
||||
|
||||
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_FORECASTS);
|
||||
|
||||
public static final Uri CONTENT_URI_WIDGET = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_FORECASTS_WIDGET);
|
||||
|
||||
|
||||
public static final String CONTENT_LIST_TYPE =
|
||||
ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_FORECASTS;
|
||||
|
||||
|
||||
public static final String CONTENT_ITEM_TYPE =
|
||||
ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_FORECASTS;
|
||||
|
||||
|
||||
public final static String TABLE_NAME = "forecasts";
|
||||
|
||||
public final static String TABLE_NAME_WIDGET = "widgetitems";
|
||||
|
||||
public final static String _ID = BaseColumns._ID;
|
||||
|
||||
public final static String COLUMN_FORECAST_NAME = "name";
|
||||
|
||||
public final static String COLUMN_WIDGET_FORECAST_ITEM = "widgetforcastitem";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather.dbfiles;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry;
|
||||
|
||||
public class ForecastDBHelper extends SQLiteOpenHelper {
|
||||
|
||||
public static final String name = "forecasts.db";
|
||||
public static final int version = 1;
|
||||
|
||||
public ForecastDBHelper(Context context) {
|
||||
super(context, name, null, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase sqLiteDatabase) {
|
||||
String SQL_CREATE_PRODUCTS_TABLE = "CREATE TABLE " + ForecastEntry.TABLE_NAME + " ("
|
||||
+ ForecastEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ ForecastEntry.COLUMN_FORECAST_NAME + " TEXT NOT NULL)";
|
||||
|
||||
sqLiteDatabase.execSQL(SQL_CREATE_PRODUCTS_TABLE);
|
||||
sqLiteDatabase.execSQL(SQL_CREATE_PRODUCTS_TABLE_2);
|
||||
}
|
||||
|
||||
private static final String SQL_CREATE_PRODUCTS_TABLE_2 = "CREATE TABLE " + ForecastEntry.TABLE_NAME_WIDGET + " ("
|
||||
+ ForecastEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ ForecastEntry.COLUMN_FORECAST_NAME + " TEXT NOT NULL, "
|
||||
+ ForecastEntry.COLUMN_WIDGET_FORECAST_ITEM + " TEXT)";
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather.dbfiles;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.dbfiles.ForecastContract.ForecastEntry;
|
||||
|
||||
public class ForecastProvider extends ContentProvider {
|
||||
|
||||
public static final String LOG_TAG = ForecastProvider.class.getSimpleName();
|
||||
|
||||
private static final int FORECASTS = 100;
|
||||
private static final int FORECAST_ID = 101;
|
||||
|
||||
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
|
||||
static {
|
||||
|
||||
sUriMatcher.addURI(ForecastContract.CONTENT_AUTHORITY, ForecastContract.PATH_FORECASTS, FORECASTS);
|
||||
|
||||
sUriMatcher.addURI(ForecastContract.CONTENT_AUTHORITY, ForecastContract.PATH_FORECASTS + "/#", FORECAST_ID);
|
||||
}
|
||||
|
||||
ForecastDBHelper mDbHelper;
|
||||
private SQLiteDatabase database;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
mDbHelper = new ForecastDBHelper(getContext());
|
||||
database = mDbHelper.getReadableDatabase();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
|
||||
Cursor cursor;
|
||||
|
||||
int match = sUriMatcher.match(uri);
|
||||
switch (match) {
|
||||
case FORECASTS:
|
||||
|
||||
cursor = database.query(ForecastEntry.TABLE_NAME, projection, selection, selectionArgs,
|
||||
null, null, sortOrder);
|
||||
break;
|
||||
case FORECAST_ID:
|
||||
|
||||
selection = ForecastEntry._ID + "=?";
|
||||
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
|
||||
cursor = database.query(ForecastEntry.TABLE_NAME, projection, selection, selectionArgs,
|
||||
null, null, sortOrder);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot query " + uri);
|
||||
}
|
||||
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
final int match = sUriMatcher.match(uri);
|
||||
switch (match) {
|
||||
case FORECASTS:
|
||||
return ForecastEntry.CONTENT_LIST_TYPE;
|
||||
case FORECAST_ID:
|
||||
return ForecastEntry.CONTENT_ITEM_TYPE;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown URI " + uri + " with match " + match);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
|
||||
|
||||
final int match = sUriMatcher.match(uri);
|
||||
switch (match) {
|
||||
case FORECASTS:
|
||||
String name = values.getAsString(ForecastEntry.COLUMN_FORECAST_NAME);
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("name required");
|
||||
}
|
||||
|
||||
SQLiteDatabase database = mDbHelper.getWritableDatabase();
|
||||
|
||||
long id = database.insert(ForecastEntry.TABLE_NAME, null, values);
|
||||
if (id == -1) {
|
||||
Log.e(LOG_TAG, "row failed " + uri);
|
||||
return null;
|
||||
}
|
||||
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return ContentUris.withAppendedId(uri, id);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Insertion is not supported for " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||
SQLiteDatabase database = mDbHelper.getWritableDatabase();
|
||||
|
||||
int rowsDeleted;
|
||||
|
||||
final int match = sUriMatcher.match(uri);
|
||||
switch (match) {
|
||||
case FORECASTS:
|
||||
rowsDeleted = database.delete(ForecastEntry.TABLE_NAME, selection, selectionArgs);
|
||||
break;
|
||||
case FORECAST_ID:
|
||||
selection = ForecastEntry._ID + "=?";
|
||||
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
|
||||
rowsDeleted = database.delete(ForecastEntry.TABLE_NAME, selection, selectionArgs);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Deletion is not supported for " + uri);
|
||||
}
|
||||
|
||||
if (rowsDeleted != 0) {
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
}
|
||||
|
||||
return rowsDeleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||
final int match = sUriMatcher.match(uri);
|
||||
switch (match) {
|
||||
case FORECASTS:
|
||||
return updateProduct(uri, contentValues, selection, selectionArgs);
|
||||
case FORECAST_ID:
|
||||
|
||||
selection = ForecastEntry._ID + "=?";
|
||||
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
|
||||
return updateProduct(uri, contentValues, selection, selectionArgs);
|
||||
default:
|
||||
throw new IllegalArgumentException("Update is not supported for " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
private int updateProduct(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
|
||||
if (values.containsKey(ForecastEntry.COLUMN_FORECAST_NAME)) {
|
||||
String name = values.getAsString(ForecastEntry.COLUMN_FORECAST_NAME);
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("Name required");
|
||||
}
|
||||
}
|
||||
|
||||
if (values.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SQLiteDatabase database = mDbHelper.getWritableDatabase();
|
||||
int rowsUpdated = database.update(ForecastEntry.TABLE_NAME, values, selection, selectionArgs);
|
||||
if (rowsUpdated != 0) {
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
}
|
||||
|
||||
return rowsUpdated;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.gms.location.FusedLocationProviderClient;
|
||||
import com.google.android.gms.location.LocationServices;
|
||||
|
||||
/**
|
||||
* Created by h_mal on 05/05/2018.
|
||||
*/
|
||||
|
||||
public class getLatLong{
|
||||
static Location location;
|
||||
public static Double longitude;
|
||||
public static Double latitude;
|
||||
|
||||
private static String TAG = getLatLong.class.getSimpleName();
|
||||
|
||||
public getLatLong(){
|
||||
super();
|
||||
}
|
||||
|
||||
public static void configLatLong(Context context) {
|
||||
|
||||
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
FusedLocationProviderClient mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context);
|
||||
if(ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
!=PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(context, "Location permission denied", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
try {
|
||||
location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
}catch (Exception e){
|
||||
Log.e("latlong error", "configLatLong: ", e);
|
||||
}finally {
|
||||
if (location == null){
|
||||
Log.i(TAG, "configLatLong: location initially was null");
|
||||
try{
|
||||
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
|
||||
}catch (Exception e){
|
||||
Log.e(TAG, "configLatLong: ", e);
|
||||
}finally {
|
||||
if (location != null){
|
||||
latitude = location.getLatitude();
|
||||
longitude = location.getLongitude();
|
||||
|
||||
Log.i(TAG, "onSuccess: Latitude:" + location.getLatitude()
|
||||
+ "\n longitude: " + location.getLongitude());
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
latitude = location.getLatitude();
|
||||
longitude = location.getLongitude();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package com.appttude.h_mal.atlas_weather;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class infopageItem {
|
||||
|
||||
private int icon;
|
||||
private String firstLine;
|
||||
private String authorUrl;
|
||||
private String authorName;
|
||||
private String siteUrl;
|
||||
private String siteName;
|
||||
|
||||
public infopageItem(int icon, @Nullable String firstLine, @Nullable String authorUrl, @Nullable String authorName,
|
||||
@Nullable String siteUrl, @Nullable String siteName) {
|
||||
this.icon = icon;
|
||||
this.firstLine = firstLine;
|
||||
this.authorUrl = authorUrl;
|
||||
this.authorName = authorName;
|
||||
this.siteUrl = siteUrl;
|
||||
this.siteName = siteName;
|
||||
}
|
||||
|
||||
public int getInfoIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public String getFirstLine() {
|
||||
return firstLine;
|
||||
}
|
||||
|
||||
public String getAuthorUrl() {
|
||||
return authorUrl;
|
||||
}
|
||||
|
||||
public String getAuthorName() {
|
||||
return authorName;
|
||||
}
|
||||
|
||||
public String getSiteUrl() {
|
||||
return siteUrl;
|
||||
}
|
||||
|
||||
public String getSiteName() {
|
||||
return siteName;
|
||||
}
|
||||
|
||||
public static class infoPageAdapter extends ArrayAdapter<infopageItem>{
|
||||
|
||||
public infoPageAdapter(@NonNull Context context, @NonNull List<infopageItem> objects) {
|
||||
super(context, 0, objects);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
View listItemView = convertView;
|
||||
|
||||
if (listItemView == null) {
|
||||
listItemView = LayoutInflater.from(getContext()).inflate(R.layout.info_dialog_item, parent,false);
|
||||
}
|
||||
|
||||
infopageItem inf = getItem(position);
|
||||
|
||||
ImageView imageView = listItemView.findViewById(R.id.infopage_icon);
|
||||
TextView firstLineTextView = listItemView.findViewById(R.id.first_part_text);
|
||||
TextView authorLinkTextView = listItemView.findViewById(R.id.author_link);
|
||||
TextView thirdLine = listItemView.findViewById(R.id.third_text);
|
||||
TextView companyWebsiteTextView = listItemView.findViewById(R.id.company_website);
|
||||
|
||||
|
||||
|
||||
if(position == 0){
|
||||
imageView.setImageResource(inf.getInfoIcon());
|
||||
firstLineTextView.setText(inf.getFirstLine());
|
||||
authorLinkTextView.setVisibility(View.GONE);
|
||||
thirdLine.setText("");
|
||||
SetUrlHyperlink(companyWebsiteTextView,inf.getSiteName(),inf.getSiteUrl());
|
||||
}else{
|
||||
imageView.setImageResource(inf.getInfoIcon());
|
||||
firstLineTextView.setText(inf.getFirstLine());
|
||||
authorLinkTextView.setVisibility(View.VISIBLE);
|
||||
thirdLine.setText("From ");
|
||||
SetUrlHyperlink(authorLinkTextView,inf.getAuthorName(), inf.getAuthorUrl());
|
||||
SetUrlHyperlink(companyWebsiteTextView,"www.flaticon.com ","www.flaticon.com" );
|
||||
}
|
||||
|
||||
|
||||
return listItemView;
|
||||
}
|
||||
|
||||
private void SetUrlHyperlink(TextView textView, String textString, final String url){
|
||||
textView.setText(textString);
|
||||
textView.setTextColor(getContext().getColor(android.R.color.holo_blue_dark));
|
||||
textView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
getContext().startActivity(myIntent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.forecast
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.DailyWeather
|
||||
import com.appttude.h_mal.atlas_weather.utils.toDayName
|
||||
import com.appttude.h_mal.atlas_weather.utils.toDayString
|
||||
import com.appttude.h_mal.atlas_weather.utils.toTime
|
||||
|
||||
data class Forecast(
|
||||
val date: String?,
|
||||
val day: String?,
|
||||
val condition: String?,
|
||||
val weatherIcon: String?,
|
||||
val mainTemp: String?,
|
||||
val minorTemp: String?,
|
||||
val averageTemp: String?,
|
||||
val windText: String?,
|
||||
val precipitation: String?,
|
||||
val humidity: String?,
|
||||
val uvi: String?,
|
||||
val sunrise: String?,
|
||||
val sunset: String?
|
||||
): Parcelable {
|
||||
|
||||
constructor(dailyWeather: DailyWeather) : this(
|
||||
dailyWeather.dt?.toDayString(),
|
||||
dailyWeather.dt?.toDayName(),
|
||||
dailyWeather.description,
|
||||
dailyWeather.icon,
|
||||
dailyWeather.max?.toInt().toString(),
|
||||
dailyWeather.min?.toInt().toString(),
|
||||
dailyWeather.average?.toInt().toString(),
|
||||
dailyWeather.windSpeed?.toInt().toString(),
|
||||
(dailyWeather.pop?.times(100)).toString(),
|
||||
dailyWeather.humidity?.toString(),
|
||||
dailyWeather.uvi?.toInt().toString(),
|
||||
dailyWeather.sunrise?.toTime(),
|
||||
dailyWeather.sunset?.toTime()
|
||||
)
|
||||
|
||||
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString()) {
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(date)
|
||||
parcel.writeString(day)
|
||||
parcel.writeString(condition)
|
||||
parcel.writeString(weatherIcon)
|
||||
parcel.writeString(mainTemp)
|
||||
parcel.writeString(minorTemp)
|
||||
parcel.writeString(averageTemp)
|
||||
parcel.writeString(windText)
|
||||
parcel.writeString(precipitation)
|
||||
parcel.writeString(humidity)
|
||||
parcel.writeString(uvi)
|
||||
parcel.writeString(sunrise)
|
||||
parcel.writeString(sunset)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<Forecast> {
|
||||
override fun createFromParcel(parcel: Parcel): Forecast {
|
||||
return Forecast(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Forecast?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.forecast
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
||||
|
||||
|
||||
data class WeatherDisplay(
|
||||
val averageTemp: Double?,
|
||||
var unit: String?,
|
||||
var location: String?,
|
||||
val iconURL: String?,
|
||||
val description: String?,
|
||||
val forecast: List<Forecast>?,
|
||||
val windSpeed: String?,
|
||||
val windDirection: String?,
|
||||
val precipitation: String?,
|
||||
val humidity: String?,
|
||||
val clouds: String?,
|
||||
val lat: Double = 0.00,
|
||||
val lon: Double = 0.00
|
||||
): Parcelable{
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readValue(Double::class.java.classLoader) as? Double,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.createTypedArrayList(Forecast),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString()) {
|
||||
}
|
||||
|
||||
constructor(weather: FullWeather) : this(
|
||||
weather.current?.temp,
|
||||
null,
|
||||
null,
|
||||
weather.current?.icon,
|
||||
weather.current?.description,
|
||||
weather.daily?.map { Forecast(it) },
|
||||
weather.current?.windSpeed?.toString(),
|
||||
weather.current?.windDeg?.toString(),
|
||||
weather.daily?.get(0)?.pop?.toString(),
|
||||
weather.current?.humidity?.toString(),
|
||||
weather.current?.clouds?.toString(),
|
||||
weather.lat,
|
||||
weather.lon
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeValue(averageTemp)
|
||||
parcel.writeString(unit)
|
||||
parcel.writeString(location)
|
||||
parcel.writeString(iconURL)
|
||||
parcel.writeString(description)
|
||||
parcel.writeTypedList(forecast)
|
||||
parcel.writeString(windSpeed)
|
||||
parcel.writeString(windDirection)
|
||||
parcel.writeString(precipitation)
|
||||
parcel.writeString(humidity)
|
||||
parcel.writeString(clouds)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<WeatherDisplay> {
|
||||
override fun createFromParcel(parcel: Parcel): WeatherDisplay {
|
||||
return WeatherDisplay(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<WeatherDisplay?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.weather
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Current
|
||||
|
||||
data class Current(
|
||||
val dt: Int? = null,
|
||||
val sunrise: Int? = null,
|
||||
val sunset: Int? = null,
|
||||
val temp: Double? = null,
|
||||
val visibility: Int? = null,
|
||||
val uvi: Double? = null,
|
||||
val pressure: Int? = null,
|
||||
val clouds: Int? = null,
|
||||
val feelsLike: Double? = null,
|
||||
val windDeg: Int? = null,
|
||||
val dewPoint: Double? = null,
|
||||
val icon: String? = null,
|
||||
val description: String? = null,
|
||||
val main: String? = null,
|
||||
val id: Int? = null,
|
||||
val humidity: Int? = null,
|
||||
val windSpeed: Double? = null
|
||||
){
|
||||
|
||||
constructor(dailyItem: Current): this(
|
||||
dailyItem.dt,
|
||||
dailyItem.sunrise,
|
||||
dailyItem.sunset,
|
||||
dailyItem.temp,
|
||||
dailyItem.visibility,
|
||||
dailyItem.uvi,
|
||||
dailyItem.pressure,
|
||||
dailyItem.clouds,
|
||||
dailyItem.feelsLike,
|
||||
dailyItem.windDeg,
|
||||
dailyItem.dewPoint,
|
||||
dailyItem.weather?.get(0)?.icon?.let { "https://openweathermap.org/img/wn/${it}@4x.png" },
|
||||
dailyItem.weather?.get(0)?.description,
|
||||
dailyItem.weather?.get(0)?.main,
|
||||
dailyItem.weather?.get(0)?.id,
|
||||
dailyItem.humidity,
|
||||
dailyItem.windSpeed
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.weather
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.DailyItem
|
||||
|
||||
|
||||
data class DailyWeather(
|
||||
val dt: Int?,
|
||||
val sunrise: Int?,
|
||||
val sunset: Int?,
|
||||
val min: Double? = null,
|
||||
val max: Double? = null,
|
||||
val average: Double? = null,
|
||||
var feelsLike: Double?,
|
||||
val pressure: Int?,
|
||||
val humidity: Int?,
|
||||
val dewPoint: Double?,
|
||||
val windSpeed: Double?,
|
||||
val windDeg: Int?,
|
||||
val icon: String? = null,
|
||||
val description: String? = null,
|
||||
val main: String? = null,
|
||||
val id: Int? = null,
|
||||
val clouds: Int?,
|
||||
val pop: Double?,
|
||||
val uvi: Double?,
|
||||
val rain: Double?
|
||||
){
|
||||
|
||||
constructor(dailyItem: DailyItem): this(
|
||||
dailyItem.dt,
|
||||
dailyItem.sunrise,
|
||||
dailyItem.sunset,
|
||||
dailyItem.temp?.min,
|
||||
dailyItem.temp?.max,
|
||||
dailyItem.temp?.day,
|
||||
dailyItem.feelsLike?.day,
|
||||
dailyItem.pressure,
|
||||
dailyItem.humidity,
|
||||
dailyItem.dewPoint,
|
||||
dailyItem.windSpeed,
|
||||
dailyItem.windDeg,
|
||||
dailyItem.weather?.get(0)?.icon?.let { "https://openweathermap.org/img/wn/${it}@4x.png" },
|
||||
dailyItem.weather?.get(0)?.description,
|
||||
dailyItem.weather?.get(0)?.main,
|
||||
dailyItem.weather?.get(0)?.id,
|
||||
dailyItem.clouds,
|
||||
dailyItem.pop,
|
||||
dailyItem.uvi,
|
||||
dailyItem.rain
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.weather
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
||||
|
||||
data class FullWeather(
|
||||
val current: Current? = null,
|
||||
val timezone: String? = null,
|
||||
val timezoneOffset: Int? = null,
|
||||
val daily: List<DailyWeather>? = null,
|
||||
val lon: Double = 0.00,
|
||||
val lat: Double = 0.00
|
||||
) {
|
||||
|
||||
constructor(weatherResponse: WeatherResponse): this(
|
||||
weatherResponse.current?.let { Current(it) },
|
||||
weatherResponse.timezone,
|
||||
weatherResponse.timezoneOffset,
|
||||
weatherResponse.daily?.map { DailyWeather(it) },
|
||||
weatherResponse.lon,
|
||||
weatherResponse.lat
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.widget
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class WidgetData(
|
||||
val location: String?,
|
||||
val icon: Bitmap?,
|
||||
val currentTemp: String?,
|
||||
val list: List<InnerWidgetData>? = listOf()
|
||||
):Parcelable{
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readString(),
|
||||
parcel.readParcelable(Bitmap::class.java.classLoader),
|
||||
parcel.readString(),
|
||||
parcel.createTypedArrayList(InnerWidgetData)
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(location)
|
||||
parcel.writeParcelable(icon, flags)
|
||||
parcel.writeString(currentTemp)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<WidgetData> {
|
||||
override fun createFromParcel(parcel: Parcel): WidgetData {
|
||||
return WidgetData(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<WidgetData?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class InnerWidgetData(
|
||||
val date: String?,
|
||||
val icon: Bitmap?,
|
||||
val currentTemp: String?
|
||||
):Parcelable{
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readString(),
|
||||
parcel.readParcelable(Bitmap::class.java.classLoader),
|
||||
parcel.readString()
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(date)
|
||||
parcel.writeParcelable(icon, flags)
|
||||
parcel.writeString(currentTemp)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<InnerWidgetData> {
|
||||
override fun createFromParcel(parcel: Parcel): InnerWidgetData {
|
||||
return InnerWidgetData(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<InnerWidgetData?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface WidgetDataImplementation{
|
||||
fun getWidgetData(): WidgetData
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.appttude.h_mal.atlas_weather.notification
|
||||
|
||||
import android.graphics.Bitmap
|
||||
|
||||
data class NotificationData(
|
||||
val temp: String,
|
||||
val icon: Bitmap
|
||||
)
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.appttude.h_mal.atlas_weather.notification
|
||||
|
||||
import android.Manifest
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.RequiresPermission
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.Repository
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
||||
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetData
|
||||
import com.appttude.h_mal.atlas_weather.model.widget.WidgetData
|
||||
import com.appttude.h_mal.atlas_weather.utils.toSmallDayName
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
|
||||
class ServicesHelper(
|
||||
private val repository: Repository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val locationProvider: LocationProvider
|
||||
) {
|
||||
|
||||
@RequiresPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
suspend fun getData(): FullWeather? {
|
||||
return try {
|
||||
val latLon = locationProvider.getLatLong()
|
||||
val result =
|
||||
repository.getWeatherFromApi(
|
||||
latLon.first.toString(),
|
||||
latLon.second.toString()
|
||||
)
|
||||
FullWeather(result)
|
||||
} catch (e: Exception) {
|
||||
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
suspend fun fetchData(): Boolean {
|
||||
if (!repository.isSearchValid(CURRENT_LOCATION)) return true
|
||||
|
||||
return try {
|
||||
// Get location
|
||||
val latLong = locationProvider.getLatLong()
|
||||
// Get weather from api
|
||||
val weather = repository
|
||||
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
|
||||
|
||||
// Save data if not null
|
||||
weather.let {
|
||||
repository.saveLastSavedAt(CURRENT_LOCATION)
|
||||
repository.saveCurrentWeatherToRoom(CURRENT_LOCATION, it)
|
||||
}
|
||||
false
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getWidgetWeather(): WidgetData? {
|
||||
return try {
|
||||
val result = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
|
||||
|
||||
result.weather.let {
|
||||
WidgetData(
|
||||
locationProvider.getLocationName(it.lat, it.lon),
|
||||
getBitmapFromUrl(it.daily?.get(0)?.icon),
|
||||
it.current?.temp?.toInt().toString()
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) { null }
|
||||
}
|
||||
|
||||
suspend fun getWidgetInnerWeather(): List<InnerWidgetData>? {
|
||||
return try {
|
||||
val result = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
|
||||
|
||||
result.weather.daily?.drop(1)?.dropLast(2)?.map{
|
||||
InnerWidgetData(
|
||||
it.dt?.toSmallDayName(),
|
||||
getBitmapFromUrl(it.icon),
|
||||
it.max?.toInt().toString()
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) { null }
|
||||
}
|
||||
|
||||
|
||||
fun getBitmapFromUrl(imageAddress: String?): Bitmap? {
|
||||
return try {
|
||||
val url = URL(imageAddress)
|
||||
BitmapFactory.decodeStream(url.openConnection().getInputStream())
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun isEnabled() = settingsRepository.isNotificationsEnabled()
|
||||
|
||||
fun getWidgetBackground(): Int {
|
||||
return if (settingsRepository.isBlackBackground()){
|
||||
Color.BLACK
|
||||
}else{
|
||||
Color.TRANSPARENT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun setFirstTimer() = settingsRepository.setFirstTime()
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import com.appttude.h_mal.atlas_weather.utils.Event
|
||||
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
||||
import com.appttude.h_mal.atlas_weather.utils.hide
|
||||
import com.appttude.h_mal.atlas_weather.utils.show
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseFragment: Fragment(){
|
||||
|
||||
// toggle visibility of progress spinner while async operations are taking place
|
||||
fun progressBarStateObserver(progressBar: View) = Observer<Event<Boolean>> {
|
||||
when(it.getContentIfNotHandled()){
|
||||
true -> {
|
||||
progressBar.show()
|
||||
}
|
||||
false -> {
|
||||
progressBar.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// display a toast when operation fails
|
||||
fun errorObserver() = Observer<Event<String>> {
|
||||
it.getContentIfNotHandled()?.let { message ->
|
||||
displayToast(message)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
fun getPermissionResult(
|
||||
permission: String,
|
||||
permissionCode: Int,
|
||||
permissionGranted: () -> Unit
|
||||
){
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(arrayOf(permission), permissionCode)
|
||||
return
|
||||
}else{
|
||||
CoroutineScope(Dispatchers.Main).launch{
|
||||
permissionGranted.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.ui.home.BaseActivity
|
||||
import com.appttude.h_mal.atlas_weather.ui.settings.UnitSettingsActivity
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import kotlinx.android.synthetic.main.activity_main_navigation.*
|
||||
|
||||
private const val PERMISSION_LOCATION = 500
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main_navigation)
|
||||
|
||||
val navView: BottomNavigationView = findViewById(R.id.nav_view)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val navHost = supportFragmentManager
|
||||
.findFragmentById(R.id.container) as NavHostFragment
|
||||
val navController = navHost.navController
|
||||
navController.setGraph(R.navigation.main_navigation)
|
||||
|
||||
setupBottomBar(navView, navController)
|
||||
|
||||
}
|
||||
|
||||
private fun setupBottomBar(navView: BottomNavigationView, navController: NavController) {
|
||||
val tabs = setOf(R.id.nav_home, R.id.nav_world)
|
||||
val appBarConfiguration = AppBarConfiguration(tabs)
|
||||
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
navView.setupWithNavController(navController)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
// Handle action bar item clicks here. The action bar will
|
||||
// automatically handle clicks on the Home/Up button, so long
|
||||
// as you specify a parent activity in AndroidManifest.xml.
|
||||
when (item.itemId) {
|
||||
R.id.action_settings -> {
|
||||
val i = Intent(this, UnitSettingsActivity::class.java)
|
||||
startActivity(i)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||
|
||||
class WeatherRecyclerAdapter(
|
||||
val itemClick: (Forecast) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var weather: WeatherDisplay? = null
|
||||
|
||||
fun addCurrent(current: WeatherDisplay){
|
||||
weather = current
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (getDataType(viewType)){
|
||||
is ViewType.Empty -> {
|
||||
val emptyViewHolder = View(parent.context)
|
||||
EmptyViewHolder(emptyViewHolder)
|
||||
}
|
||||
is ViewType.Current -> {
|
||||
val viewCurrent = parent.generateView(R.layout.list_item4)
|
||||
ViewHolderCurrent(viewCurrent)
|
||||
}
|
||||
is ViewType.Forecast -> {
|
||||
val viewForecast = parent.generateView(R.layout.list_item_layout2)
|
||||
ViewHolderForecast(viewForecast)
|
||||
}
|
||||
is ViewType.Further -> {
|
||||
val viewFurther = parent.generateView(R.layout.list_item3)
|
||||
ViewHolderFurtherDetails(viewFurther)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ViewType{
|
||||
object Empty : ViewType()
|
||||
object Current : ViewType()
|
||||
object Forecast : ViewType()
|
||||
object Further : ViewType()
|
||||
}
|
||||
|
||||
private fun getDataType(type: Int): ViewType{
|
||||
return when (type){
|
||||
0 -> ViewType.Empty
|
||||
1 -> ViewType.Current
|
||||
2 -> ViewType.Forecast
|
||||
3 -> ViewType.Further
|
||||
else -> ViewType.Empty
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (weather == null) return 0
|
||||
|
||||
return when(position){
|
||||
0 -> 1
|
||||
in 1 until itemCount -2 -> 2
|
||||
itemCount - 1 -> 3
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (getDataType(getItemViewType(position))){
|
||||
is ViewType.Empty -> {
|
||||
holder as EmptyViewHolder
|
||||
|
||||
}
|
||||
is ViewType.Current -> {
|
||||
val viewHolderCurrent = holder as ViewHolderCurrent
|
||||
viewHolderCurrent.bindData(weather)
|
||||
}
|
||||
is ViewType.Forecast -> {
|
||||
val viewHolderForecast = holder as ViewHolderForecast
|
||||
|
||||
weather?.forecast?.get(position)?.let { i ->
|
||||
viewHolderForecast.bindView(i)
|
||||
viewHolderForecast.itemView.setOnClickListener {
|
||||
itemClick(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
is ViewType.Further -> {
|
||||
val viewHolderCurrent = holder as ViewHolderFurtherDetails
|
||||
viewHolderCurrent.bindData(weather)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
if (weather == null) return 0
|
||||
return 2 + (weather?.forecast?.size?: 0)
|
||||
}
|
||||
|
||||
internal class ViewHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
|
||||
|
||||
var locationTV: TextView = listItemView.findViewById(R.id.location_main_4)
|
||||
var conditionTV: TextView = listItemView.findViewById(R.id.condition_main_4)
|
||||
var weatherIV: ImageView = listItemView.findViewById(R.id.icon_main_4)
|
||||
var avgTempTV: TextView = listItemView.findViewById(R.id.temp_main_4)
|
||||
var tempUnit: TextView = listItemView.findViewById(R.id.temp_unit_4)
|
||||
|
||||
fun bindData(weather: WeatherDisplay?){
|
||||
locationTV.text = weather?.location
|
||||
conditionTV.text = weather?.description
|
||||
weatherIV.loadImage(weather?.iconURL)
|
||||
avgTempTV.text = weather?.averageTemp?.toInt().toString()
|
||||
tempUnit.text = weather?.unit
|
||||
}
|
||||
}
|
||||
|
||||
internal class ViewHolderForecast(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var dateTV: TextView = itemView.findViewById(R.id.list_date)
|
||||
var dayTV: TextView = itemView.findViewById(R.id.list_day)
|
||||
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
|
||||
var weatherIV: ImageView = itemView.findViewById(R.id.list_icon)
|
||||
var mainTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
|
||||
var minorTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
|
||||
|
||||
fun bindView(forecast: Forecast?) {
|
||||
dateTV.text = forecast?.date
|
||||
dayTV.text = forecast?.day
|
||||
conditionTV.text = forecast?.condition
|
||||
weatherIV.loadImage(forecast?.weatherIcon)
|
||||
mainTempTV.text = forecast?.mainTemp
|
||||
minorTempTV.text = forecast?.minorTemp
|
||||
}
|
||||
}
|
||||
|
||||
internal class ViewHolderFurtherDetails(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var windSpeed: TextView = itemView.findViewById(R.id.windspeed)
|
||||
var windDirection: TextView = itemView.findViewById(R.id.winddirection)
|
||||
var precipitation: TextView = itemView.findViewById(R.id.precip_)
|
||||
var humidity: TextView = itemView.findViewById(R.id.humidity_)
|
||||
var clouds: TextView = itemView.findViewById(R.id.clouds_)
|
||||
|
||||
fun bindData(weather: WeatherDisplay?){
|
||||
windSpeed.text = weather?.windSpeed
|
||||
windDirection.text = weather?.windDirection
|
||||
precipitation.text = weather?.precipitation
|
||||
humidity.text = weather?.humidity
|
||||
clouds.text = weather?.clouds
|
||||
}
|
||||
}
|
||||
|
||||
internal class EmptyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||
import com.appttude.h_mal.atlas_weather.utils.navigateTo
|
||||
import kotlinx.android.synthetic.main.fragment_main.*
|
||||
|
||||
|
||||
class WorldItemFragment : Fragment() {
|
||||
private var param1: WeatherDisplay? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
param1 = WorldItemFragmentArgs.fromBundle(requireArguments()).weatherDisplay
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val recyclerAdapter = WeatherRecyclerAdapter {
|
||||
val directions =
|
||||
WorldItemFragmentDirections.actionWorldItemFragmentToFurtherDetailsFragment(it)
|
||||
navigateTo(directions)
|
||||
}
|
||||
|
||||
param1?.let { recyclerAdapter.addCurrent(it) }
|
||||
|
||||
forecast_listview.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = recyclerAdapter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.details
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||
import kotlinx.android.synthetic.main.activity_further_info.*
|
||||
|
||||
|
||||
private const val WEATHER = "param1"
|
||||
/**
|
||||
* A simple [Fragment] subclass.
|
||||
* Use the [FurtherInfoFragment.newInstance] factory method to
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
class FurtherInfoFragment : Fragment() {
|
||||
private var param1: Forecast? = null
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
param1 = FurtherInfoFragmentArgs.fromBundle(requireArguments()).forecast
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.activity_further_info, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
maxtemp.text = param1?.mainTemp
|
||||
averagetemp.text = param1?.averageTemp
|
||||
minimumtemp.text = param1?.minorTemp
|
||||
windtext.text = param1?.windText
|
||||
preciptext.text = param1?.precipitation
|
||||
humiditytext.text = param1?.humidity
|
||||
uvtext.text = param1?.uvi
|
||||
sunrisetext.text = param1?.sunrise
|
||||
sunsettext.text = param1?.sunset
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseActivity : AppCompatActivity(){
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
fun getPermissionResult(
|
||||
permission: String,
|
||||
permissionCode: Int,
|
||||
permissionGranted: () -> Unit
|
||||
){
|
||||
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(arrayOf(permission), permissionCode)
|
||||
return
|
||||
}else{
|
||||
CoroutineScope(Dispatchers.Main).launch{
|
||||
permissionGranted.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.observe
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
|
||||
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
|
||||
import com.appttude.h_mal.atlas_weather.ui.WeatherRecyclerAdapter
|
||||
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
||||
import com.appttude.h_mal.atlas_weather.utils.navigateTo
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.MainViewModel
|
||||
import kotlinx.android.synthetic.main.activity_add_forecast.*
|
||||
import kotlinx.android.synthetic.main.fragment_main.*
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.x.kodein
|
||||
import org.kodein.di.generic.instance
|
||||
|
||||
|
||||
/**
|
||||
* A simple [Fragment] subclass.
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
class HomeFragment : BaseFragment(), KodeinAware {
|
||||
override val kodein by kodein()
|
||||
private val factory by instance<ApplicationViewModelFactory>()
|
||||
|
||||
private val viewModel by activityViewModels<MainViewModel> { factory }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val recyclerAdapter = WeatherRecyclerAdapter {
|
||||
val directions =
|
||||
HomeFragmentDirections.actionHomeFragmentToFurtherDetailsFragment(it)
|
||||
navigateTo(directions)
|
||||
}
|
||||
|
||||
forecast_listview.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = recyclerAdapter
|
||||
}
|
||||
|
||||
getPermissionResult(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION_PERMISSION_REQUEST){
|
||||
viewModel.fetchData()
|
||||
}
|
||||
|
||||
swipe_refresh.apply {
|
||||
setOnRefreshListener {
|
||||
viewModel.fetchData()
|
||||
isRefreshing = true
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.weatherLiveData.observe(viewLifecycleOwner) {
|
||||
recyclerAdapter.addCurrent(it)
|
||||
}
|
||||
|
||||
viewModel.operationState.observe(viewLifecycleOwner, progressBarStateObserver(progressBar))
|
||||
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
|
||||
|
||||
viewModel.operationState.observe(viewLifecycleOwner){
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == LOCATION_PERMISSION_REQUEST) {
|
||||
if (grantResults.isNotEmpty()
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
viewModel.fetchData()
|
||||
displayToast("Permission granted")
|
||||
} else {
|
||||
displayToast("Permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.settings
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceActivity
|
||||
import android.preference.PreferenceFragment
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.notification.NotificationReceiver
|
||||
import com.appttude.h_mal.atlas_weather.widget.NewAppWidget
|
||||
import java.util.*
|
||||
|
||||
|
||||
class UnitSettingsActivity : PreferenceActivity() {
|
||||
private val TAG = javaClass.simpleName
|
||||
private var prefListener: OnSharedPreferenceChangeListener? = null
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
PreferenceManager.setDefaultValues(this, R.xml.prefs, false)
|
||||
fragmentManager.beginTransaction().replace(android.R.id.content, MyPreferenceFragment()).commit()
|
||||
|
||||
//listener on changed sort order preference:
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
|
||||
prefListener = OnSharedPreferenceChangeListener { _, key ->
|
||||
if (key == "temp_units") {
|
||||
val intent = Intent(baseContext, NewAppWidget::class.java)
|
||||
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
val ids = AppWidgetManager.getInstance(application).getAppWidgetIds(ComponentName(application, NewAppWidget::class.java))
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
if (key == "notif_boolean") {
|
||||
setupNotificationBroadcaster(baseContext)
|
||||
}
|
||||
|
||||
if (key == "widget_black_background"){
|
||||
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
|
||||
val widgetManager = AppWidgetManager.getInstance(this)
|
||||
val ids = widgetManager.getAppWidgetIds(ComponentName(this, NewAppWidget::class.java))
|
||||
AppWidgetManager.getInstance(this).notifyAppWidgetViewDataChanged(ids, R.id.whole_widget_view)
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
prefs.registerOnSharedPreferenceChangeListener(prefListener)
|
||||
}
|
||||
|
||||
fun setupNotificationBroadcaster(context: Context) {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
val notificationIntent = Intent(context, NotificationReceiver::class.java)
|
||||
val broadcast = PendingIntent.getBroadcast(context, 100, notificationIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val cal: Calendar = Calendar.getInstance()
|
||||
cal.set(Calendar.HOUR_OF_DAY, 6)
|
||||
cal.set(Calendar.MINUTE, 8)
|
||||
cal.set(Calendar.SECOND, 5)
|
||||
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.timeInMillis, AlarmManager.INTERVAL_DAY, broadcast)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
|
||||
// Log.i(TAG, "onSharedPreferenceChanged: " + s);
|
||||
// if (s == "temp_units"){
|
||||
// Intent intent = new Intent(getBaseContext(), NewAppWidget.class);
|
||||
// intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
//
|
||||
// int[] ids = AppWidgetManager.getInstance(getApplication()).getAppWidgetIds(new ComponentName(getApplication(), NewAppWidget.class));
|
||||
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
|
||||
// sendBroadcast(intent);
|
||||
// }
|
||||
// }
|
||||
class MyPreferenceFragment : PreferenceFragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
addPreferencesFromResource(R.xml.prefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.world
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.observe
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
|
||||
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
||||
import com.appttude.h_mal.atlas_weather.utils.goBack
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
|
||||
import kotlinx.android.synthetic.main.activity_add_forecast.*
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.x.kodein
|
||||
import org.kodein.di.generic.instance
|
||||
|
||||
|
||||
class AddLocationFragment : BaseFragment(), KodeinAware {
|
||||
override val kodein by kodein()
|
||||
private val factory by instance<ApplicationViewModelFactory>()
|
||||
|
||||
private val viewModel by viewModels<WorldViewModel> { factory }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.activity_add_forecast, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
submit.setOnClickListener {
|
||||
val locationName = location_name_tv.text?.trim()?.toString()
|
||||
if (locationName.isNullOrBlank()){
|
||||
submit.error = "Location cannot be blank"
|
||||
return@setOnClickListener
|
||||
}
|
||||
viewModel.fetchDataForSingleLocation(locationName)
|
||||
}
|
||||
|
||||
viewModel.operationState.observe(viewLifecycleOwner, progressBarStateObserver(progressBar))
|
||||
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
|
||||
|
||||
viewModel.operationComplete.observe(viewLifecycleOwner) {
|
||||
it?.getContentIfNotHandled()?.let {message ->
|
||||
displayToast(message)
|
||||
}
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.world
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.observe
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
|
||||
import com.appttude.h_mal.atlas_weather.utils.navigateTo
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
|
||||
import kotlinx.android.synthetic.main.fragment_add_location.*
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.x.kodein
|
||||
import org.kodein.di.generic.instance
|
||||
|
||||
|
||||
/**
|
||||
* A simple [Fragment] subclass.
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
class WorldFragment : BaseFragment(), KodeinAware {
|
||||
override val kodein by kodein()
|
||||
private val factory by instance<ApplicationViewModelFactory>()
|
||||
|
||||
val viewModel by viewModels<WorldViewModel> { factory }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_add_location, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val recyclerAdapter = WorldRecyclerAdapter{
|
||||
val direction =
|
||||
WorldFragmentDirections.actionWorldFragmentToWorldItemFragment(it)
|
||||
navigateTo(direction)
|
||||
}
|
||||
|
||||
world_recycler.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = recyclerAdapter
|
||||
}
|
||||
|
||||
viewModel.weatherLiveData.observe(viewLifecycleOwner) {
|
||||
recyclerAdapter.addCurrent(it)
|
||||
}
|
||||
|
||||
floatingActionButton.setOnClickListener{
|
||||
navigateTo(R.id.action_worldFragment_to_addLocationFragment)
|
||||
}
|
||||
|
||||
viewModel.operationState.observe(viewLifecycleOwner, progressBarStateObserver(progressBar2))
|
||||
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
viewModel.fetchAllLocations()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.world
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||
|
||||
class WorldRecyclerAdapter(
|
||||
val itemClick: (WeatherDisplay) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var weather: MutableList<WeatherDisplay> = mutableListOf()
|
||||
|
||||
fun addCurrent(current: List<WeatherDisplay>){
|
||||
weather.clear()
|
||||
weather.addAll(current)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (getDataType(viewType)){
|
||||
is ViewType.Empty -> {
|
||||
val emptyViewHolder = View(parent.context)
|
||||
EmptyViewHolder(emptyViewHolder)
|
||||
}
|
||||
is ViewType.Current -> {
|
||||
val viewCurrent = parent.generateView(R.layout.db_list_item)
|
||||
WorldHolderCurrent(viewCurrent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ViewType{
|
||||
object Empty : ViewType()
|
||||
object Current : ViewType()
|
||||
}
|
||||
|
||||
private fun getDataType(type: Int): ViewType{
|
||||
return when (type){
|
||||
0 -> ViewType.Empty
|
||||
1 -> ViewType.Current
|
||||
else -> ViewType.Empty
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (weather.isEmpty()) 0 else 1
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (getDataType(getItemViewType(position))){
|
||||
is ViewType.Empty -> {
|
||||
holder as EmptyViewHolder
|
||||
|
||||
}
|
||||
is ViewType.Current -> {
|
||||
val viewHolderCurrent = holder as WorldHolderCurrent
|
||||
viewHolderCurrent.bindData(weather[position])
|
||||
viewHolderCurrent.itemView.setOnClickListener {
|
||||
itemClick.invoke(weather[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (weather.size == 0) 1 else weather.size
|
||||
}
|
||||
|
||||
internal class WorldHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
|
||||
|
||||
var locationTV: TextView = listItemView.findViewById(R.id.db_location)
|
||||
var conditionTV: TextView = listItemView.findViewById(R.id.db_condition)
|
||||
var weatherIV: ImageView = listItemView.findViewById(R.id.db_icon)
|
||||
var avgTempTV: TextView = listItemView.findViewById(R.id.db_main_temp)
|
||||
var tempUnit: TextView = listItemView.findViewById(R.id.db_minor_temp)
|
||||
|
||||
fun bindData(weather: WeatherDisplay?){
|
||||
locationTV.text = weather?.location
|
||||
conditionTV.text = weather?.description
|
||||
weatherIV.loadImage(weather?.iconURL)
|
||||
avgTempTV.text = weather?.forecast?.get(0)?.mainTemp
|
||||
tempUnit.text = weather?.unit
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal class EmptyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.appttude.h_mal.atlas_weather.utils
|
||||
|
||||
/**
|
||||
* Used with livedata<T> to make observation lifecycle aware
|
||||
* Display livedata response only once
|
||||
*/
|
||||
open class Event<out T>(private val content: T) {
|
||||
|
||||
var hasBeenHandled = false
|
||||
private set // Allow external read but not write
|
||||
|
||||
/**
|
||||
* Returns the content and prevents its use again.
|
||||
*/
|
||||
fun getContentIfNotHandled(): T? {
|
||||
return if (hasBeenHandled) {
|
||||
null
|
||||
} else {
|
||||
hasBeenHandled = true
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.appttude.h_mal.atlas_weather.utils
|
||||
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.Navigation
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
|
||||
fun Fragment.navigateToFragment(newFragment: Fragment){
|
||||
childFragmentManager.beginTransaction()
|
||||
.add(R.id.container, newFragment)
|
||||
.commit()
|
||||
}
|
||||
|
||||
|
||||
fun View.navigateTo(navigationId: Int) {
|
||||
Navigation.findNavController(this).navigate(navigationId)
|
||||
}
|
||||
|
||||
fun View.navigateTo(navDirections: NavDirections) {
|
||||
Navigation.findNavController(this).navigate(navDirections)
|
||||
}
|
||||
|
||||
fun Fragment.navigateTo(navigationId: Int) {
|
||||
Navigation.findNavController(requireView()).navigate(navigationId)
|
||||
}
|
||||
|
||||
fun Fragment.navigateTo(navDirections: NavDirections) {
|
||||
Navigation.findNavController(requireView()).navigate(navDirections)
|
||||
}
|
||||
|
||||
fun Fragment.goBack() = Navigation.findNavController(requireView()).popBackStack()
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.appttude.h_mal.atlas_weather.utils
|
||||
|
||||
|
||||
import android.os.Build
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.OffsetTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
|
||||
fun Int.toDayString(): String {
|
||||
return try {
|
||||
val date = Date(this.makeMilliseconds())
|
||||
val format = SimpleDateFormat("MMM d", Locale.getDefault())
|
||||
format.format(date)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
"Unable to parse date"
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.makeMilliseconds(): Long = this * 1000L
|
||||
|
||||
fun Int.toDayName(): String {
|
||||
return try {
|
||||
val date = Date(this.makeMilliseconds())
|
||||
val format = SimpleDateFormat("EEEE", Locale.getDefault())
|
||||
format.format(date)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
"Unable to parse date"
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.toSmallDayName(): String {
|
||||
return try {
|
||||
val date = Date(this.makeMilliseconds())
|
||||
val format = SimpleDateFormat("EEE", Locale.getDefault())
|
||||
format.format(date)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
"Unable to parse date"
|
||||
}
|
||||
}
|
||||
|
||||
fun Int?.toTime(): String? {
|
||||
return this?.makeMilliseconds()?.let {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
OffsetTime.ofInstant(Instant.ofEpochMilli(it), ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("HH:mm"))
|
||||
} else {
|
||||
val date = Date(it)
|
||||
val format = SimpleDateFormat("HH:mm", Locale.getDefault())
|
||||
format.format(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.appttude.h_mal.atlas_weather.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
fun View.show() {
|
||||
this.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
fun View.hide() {
|
||||
this.visibility = View.GONE
|
||||
}
|
||||
|
||||
fun Context.displayToast(message: String) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun Fragment.displayToast(message: String) {
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun ImageView.loadImage(url: String?){
|
||||
Picasso.get()
|
||||
.load(url)
|
||||
.placeholder(R.mipmap.ic_launcher)
|
||||
.into(this)
|
||||
}
|
||||
|
||||
fun ViewGroup.generateView(layoutId: Int): View = LayoutInflater
|
||||
.from(context)
|
||||
.inflate(layoutId, this, false)
|
||||
|
||||
fun ImageView.loadImage(url: String?, height: Int, width: Int){
|
||||
Picasso.get()
|
||||
.load(url)
|
||||
.resize(width, height)
|
||||
.centerCrop()
|
||||
.placeholder(R.mipmap.ic_launcher)
|
||||
.into(this)
|
||||
}
|
||||
|
||||
fun SearchView.onSubmitListener(searchSubmit: (String) -> Unit) {
|
||||
this.setOnQueryTextListener(object :
|
||||
SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(s: String): Boolean {
|
||||
searchSubmit.invoke(s)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(s: String): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.appttude.h_mal.atlas_weather.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
||||
|
||||
class ApplicationViewModelFactory(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: RepositoryImpl
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
with(modelClass){
|
||||
return when{
|
||||
isAssignableFrom(WorldViewModel::class.java) -> WorldViewModel(locationProvider, repository)
|
||||
isAssignableFrom(MainViewModel::class.java) -> MainViewModel(locationProvider, repository)
|
||||
else -> throw IllegalArgumentException("Unknown ViewModel class")
|
||||
} as T
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.appttude.h_mal.atlas_weather.viewmodel
|
||||
|
||||
import android.Manifest
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.Repository
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.Current
|
||||
import com.appttude.h_mal.atlas_weather.utils.Event
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
|
||||
class MainViewModel(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: Repository
|
||||
): ViewModel(){
|
||||
|
||||
val currentWeatherLiveData = MutableLiveData<Current>()
|
||||
val weatherLiveData = MutableLiveData<WeatherDisplay>()
|
||||
|
||||
val operationState = MutableLiveData<Event<Boolean>>()
|
||||
val operationError = MutableLiveData<Event<String>>()
|
||||
|
||||
init {
|
||||
repository.loadCurrentWeatherFromRoom(CURRENT_LOCATION).observeForever {
|
||||
it?.weather?.let { data ->
|
||||
val list= WeatherDisplay(data).apply {
|
||||
unit = "°C"
|
||||
location = locationProvider.getLocationName(data.lat, data.lon)
|
||||
}
|
||||
|
||||
weatherLiveData.postValue(list)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(value = Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
fun fetchData(){
|
||||
if (!repository.isSearchValid(CURRENT_LOCATION)) return
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
operationState.postValue(Event(true))
|
||||
try {
|
||||
// Get location
|
||||
val latLong = locationProvider.getLatLong()
|
||||
// Get weather from api
|
||||
val weather = repository
|
||||
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
|
||||
|
||||
// Save data if not null
|
||||
weather.let {
|
||||
repository.saveLastSavedAt(CURRENT_LOCATION)
|
||||
repository.saveCurrentWeatherToRoom(CURRENT_LOCATION, it)
|
||||
return@launch
|
||||
}
|
||||
}catch (e: IOException){
|
||||
operationError.postValue(Event(e.message!!))
|
||||
}finally {
|
||||
operationState.postValue(Event(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.appttude.h_mal.atlas_weather.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
||||
|
||||
class MainViewModelFactory(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: RepositoryImpl
|
||||
) : ViewModelProvider.Factory{
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
|
||||
return (MainViewModel(locationProvider, repository)) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.appttude.h_mal.atlas_weather.viewmodel
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.Repository
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
||||
import com.appttude.h_mal.atlas_weather.utils.Event
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class WorldViewModel(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: Repository
|
||||
) : ViewModel() {
|
||||
|
||||
val weatherLiveData = MutableLiveData<List<WeatherDisplay>>()
|
||||
|
||||
val operationState = MutableLiveData<Event<Boolean>>()
|
||||
val operationError = MutableLiveData<Event<String>>()
|
||||
|
||||
val operationComplete = MutableLiveData<Event<String>>()
|
||||
|
||||
private val weatherListLiveData = repository.loadAllWeatherExceptCurrentFromRoom()
|
||||
|
||||
init {
|
||||
weatherListLiveData.observeForever {
|
||||
val list = it.map { data ->
|
||||
WeatherDisplay(data.weather).apply {
|
||||
unit = "°C"
|
||||
location = locationProvider.getLocationName(data.weather.lat, data.weather.lon)
|
||||
}
|
||||
}
|
||||
weatherLiveData.postValue(list)
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchDataForSingleLocation(locationName: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
operationState.postValue(Event(true))
|
||||
|
||||
if (repository.getSavedLocations().contains(locationName)){
|
||||
operationError.postValue(Event("$locationName already exists"))
|
||||
return@launch
|
||||
}
|
||||
|
||||
try {
|
||||
// Get location
|
||||
val latLong =
|
||||
locationProvider.getLatLongFromLocationString(locationName)
|
||||
// Get weather from api
|
||||
val weather = repository
|
||||
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
|
||||
|
||||
// Save data if not null
|
||||
weather.let {
|
||||
repository.saveCurrentWeatherToRoom(locationName, it)
|
||||
operationComplete.postValue(Event("$locationName saved"))
|
||||
return@launch
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
operationError.postValue(Event(e.message!!))
|
||||
} finally {
|
||||
operationState.postValue(Event(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchAllLocations() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
operationState.postValue(Event(true))
|
||||
try {
|
||||
val list = mutableListOf<EntityItem>()
|
||||
weatherLiveData.value?.forEach { weatherItem ->
|
||||
// If search not valid move onto next in loop
|
||||
weatherItem.location?.let {
|
||||
if (!repository.isSearchValid(it)) return@forEach
|
||||
}
|
||||
|
||||
try {
|
||||
val weather = repository
|
||||
.getWeatherFromApi(weatherItem.lat.toString(), weatherItem.lon.toString())
|
||||
|
||||
weatherItem.location?.let { loc ->
|
||||
repository.saveLastSavedAt(loc)
|
||||
list.add(EntityItem(loc, FullWeather(weather)))
|
||||
}
|
||||
} catch (e: IOException) { }
|
||||
}
|
||||
repository.saveWeatherListToRoom(list)
|
||||
} catch (e: IOException) {
|
||||
operationError.postValue(Event(e.message!!))
|
||||
} finally {
|
||||
operationState.postValue(Event(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.appttude.h_mal.atlas_weather.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
||||
|
||||
class WorldViewModelFactory(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: RepositoryImpl
|
||||
) : ViewModelProvider.Factory{
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(WorldViewModel::class.java)) {
|
||||
return (WorldViewModel(locationProvider, repository)) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.appttude.h_mal.atlas_weather.widget
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
import android.app.TaskStackBuilder
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.RemoteViews
|
||||
import androidx.annotation.LayoutRes
|
||||
|
||||
abstract class BaseWidgetClass : AppWidgetProvider(){
|
||||
|
||||
fun createRemoteView(context: Context, @LayoutRes id: Int): RemoteViews {
|
||||
return RemoteViews(context.packageName, id)
|
||||
}
|
||||
|
||||
fun createUpdatePendingIntent(context: Context, appWidgetId: Int): PendingIntent? {
|
||||
val seconds = (System.currentTimeMillis() / 1000L).toInt()
|
||||
val intentUpdate = Intent(context, this::class.java)
|
||||
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
val idArray = intArrayOf(appWidgetId)
|
||||
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
|
||||
return PendingIntent.getBroadcast(
|
||||
context, seconds, intentUpdate,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
fun <T: Activity> createClickingPendingIntent(context: Context, activityClass: Class<T>): PendingIntent {
|
||||
val clickIntentTemplate = Intent(context, activityClass)
|
||||
|
||||
return TaskStackBuilder.create(context)
|
||||
.addNextIntentWithParentStack(clickIntentTemplate)
|
||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
}
|
||||
8
app/src/main/res/drawable/card_outlined.xml
Normal file
8
app/src/main/res/drawable/card_outlined.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<solid android:color="#F00" />
|
||||
<size android:width="10dp"/>
|
||||
<padding android:bottom="0dp" android:left="0dp" android:right="0dp" android:top="0dp"/>
|
||||
<corners android:topLeftRadius="5dp" android:bottomLeftRadius="5dp"
|
||||
android:topRightRadius="0.1dp" android:bottomRightRadius="0.1dp"/>
|
||||
</shape>
|
||||
5
app/src/main/res/drawable/ic_baseline_home_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_home_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_public_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_public_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
|
||||
</vector>
|
||||
@@ -3,17 +3,17 @@
|
||||
android:height="275dp"
|
||||
android:viewportWidth="94.0"
|
||||
android:viewportHeight="275.0">
|
||||
<!--<path-->
|
||||
<!--android:fillColor="#FF000000"-->
|
||||
<!--android:pathData="M75.1515587070021,188.25562584016225 V29.53767503746845 c0,-14.982132974790945 -11.77674089383421,-26.46075177701539 -27.147989048437097,-26.46075177701539 c-15.371248154602887,0 -27.147989048437097,12.354497345366072 -27.147989048437097,26.46075177701539 v158.672312920709 c-10.878114078642042,7.929466747673202 -18.067128600179394,21.15938164747398 -18.067128600179394,35.2656360791233 c0,24.66289582004048 19.911678378731743,44.07052038123121-->
|
||||
<!--45.21511764861649,44.07052038123121 c25.303439269884752,0 45.21464468713481,-19.407624561190733 45.21464468713481,-44.07052038123121 C93.21821434569982,209.36936960565146 85.98237663747612,196.1850925878354 75.1515587070021,188.25562584016225 zM48.003569658565006,258.78689799840873 c-19.911678378731743,0 -36.1815533485268,-15.857089540518738 -36.1815533485268,-35.2656360791233 c0,-13.229914899800779 7.236310669705359,-24.66289582004048 18.067128600179394,-30.83922251530968-->
|
||||
<!--V29.53767503746845 c0,-9.680762845249534 8.134937484897527,-17.60976860421582 18.067128600179394,-17.60976860421582 s18.06665563869771,7.929005758966285 18.06665563869771,17.60976860421582 v163.09734351840189 c10.878114078642042,6.177248672683036 18.06760156166107,17.610229592922735 18.06760156166107,30.840144492723518 C84.1851230070918,242.88278760978457 67.91477507581507,258.78689799840873 48.003569658565006,258.78689799840873 z"/>-->
|
||||
<!--<path-->
|
||||
<!--android:name="inner"-->
|
||||
<!--android:fillColor="#ff0000"-->
|
||||
<!--android:pathData="M305.949,424.6c0-1.9,0-1.9,0-3.799v-95.602c0-11.5-7.6-19.1-19.099-19.1c-11.5,0-19.1,7.6-19.1,19.1v95.602-->
|
||||
<!--c0,1.898,0,1.898,0,3.799c-23,7.701-38.2,28.701-38.2,53.5c0,32.5,24.9,57.4,57.4,57.4c32.499,0,57.399-24.9,57.399-57.4-->
|
||||
<!--C344.25,453.301,328.949,432.301,305.949,424.6z"/>-->
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M75.1515587070021,188.25562584016225 V29.53767503746845 c0,-14.982132974790945 -11.77674089383421,-26.46075177701539 -27.147989048437097,-26.46075177701539 c-15.371248154602887,0 -27.147989048437097,12.354497345366072 -27.147989048437097,26.46075177701539 v158.672312920709 c-10.878114078642042,7.929466747673202 -18.067128600179394,21.15938164747398 -18.067128600179394,35.2656360791233 c0,24.66289582004048 19.911678378731743,44.07052038123121
|
||||
45.21511764861649,44.07052038123121 c25.303439269884752,0 45.21464468713481,-19.407624561190733 45.21464468713481,-44.07052038123121 C93.21821434569982,209.36936960565146 85.98237663747612,196.1850925878354 75.1515587070021,188.25562584016225 zM48.003569658565006,258.78689799840873 c-19.911678378731743,0 -36.1815533485268,-15.857089540518738 -36.1815533485268,-35.2656360791233 c0,-13.229914899800779 7.236310669705359,-24.66289582004048 18.067128600179394,-30.83922251530968
|
||||
V29.53767503746845 c0,-9.680762845249534 8.134937484897527,-17.60976860421582 18.067128600179394,-17.60976860421582 s18.06665563869771,7.929005758966285 18.06665563869771,17.60976860421582 v163.09734351840189 c10.878114078642042,6.177248672683036 18.06760156166107,17.610229592922735 18.06760156166107,30.840144492723518 C84.1851230070918,242.88278760978457 67.91477507581507,258.78689799840873 48.003569658565006,258.78689799840873 z"/>
|
||||
<path
|
||||
android:name="inner"
|
||||
android:fillColor="#ff0000"
|
||||
android:pathData="M305.949,424.6c0-1.9,0-1.9,0-3.799v-95.602c0-11.5-7.6-19.1-19.099-19.1c-11.5,0-19.1,7.6-19.1,19.1v95.602
|
||||
c0,1.898,0,1.898,0,3.799c-23,7.701-38.2,28.701-38.2,53.5c0,32.5,24.9,57.4,57.4,57.4c32.499,0,57.399-24.9,57.399-57.4
|
||||
C344.25,453.301,328.949,432.301,305.949,424.6z"/>
|
||||
<path android:fillColor="#000000"
|
||||
android:pathData="M75.1515587070021,188.25562584016225 V29.53767503746845 c0,-14.982132974790945 -11.77674089383421,-26.46075177701539 -27.147989048437097,-26.46075177701539 c-15.371248154602887,0 -27.147989048437097,12.354497345366072 -27.147989048437097,26.46075177701539 v158.672312920709 c-10.878114078642042,7.929466747673202 -18.067128600179394,21.15938164747398 -18.067128600179394,35.2656360791233 c0,24.66289582004048 19.911678378731743,44.07052038123121 45.21511764861649,44.07052038123121 c25.303439269884752,0 45.21464468713481,-19.407624561190733 45.21464468713481,-44.07052038123121 C93.21821434569982,209.36936960565146 85.98237663747612,196.1850925878354 75.1515587070021,188.25562584016225 zM48.003569658565006,258.78689799840873 c-19.911678378731743,0 -36.1815533485268,-15.857089540518738 -36.1815533485268,-35.2656360791233 c0,-13.229914899800779 7.236310669705359,-24.66289582004048 18.067128600179394,-30.83922251530968 V29.53767503746845 c0,-9.680762845249534 8.134937484897527,-17.60976860421582 18.067128600179394,-17.60976860421582 s18.06665563869771,7.929005758966285 18.06665563869771,17.60976860421582 v163.09734351840189 c10.878114078642042,6.177248672683036 18.06760156166107,17.610229592922735 18.06760156166107,30.840144492723518 C84.1851230070918,242.88278760978457 67.91477507581507,258.78689799840873 48.003569658565006,258.78689799840873 z"/>
|
||||
<path
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".AddForecast">
|
||||
tools:context=".legacy.ui.AddForecastActivity">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_add"
|
||||
@@ -24,7 +23,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="@dimen/activity_horizontal_margin">
|
||||
@@ -55,13 +54,22 @@
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_margin="12dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:layout_marginBottom="36dp"
|
||||
android:background="@drawable/button_layout"
|
||||
android:text="@string/submit"
|
||||
android:textColor="#ffffff"
|
||||
android:textStyle="bold"
|
||||
style="?android:attr/borderlessButtonStyle"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".FurtherInfoActivity">
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -14,14 +13,16 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thermom"
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="12dp"
|
||||
android:layout_weight="1"
|
||||
android:tint="@color/colour_five"
|
||||
app:srcCompat="@drawable/somethingnew" />
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/somethingnew"
|
||||
style="@style/icon_style__further_deatils" />
|
||||
</FrameLayout>
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
@@ -30,10 +31,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true">
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
@@ -48,14 +45,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="Maximum: " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/maxtemp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="85%"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
@@ -66,14 +63,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="Average: " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/averagetemp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="11mm"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
@@ -84,14 +81,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="Minimum: " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/minimumtemp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="11mm"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
@@ -106,14 +103,16 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="12dp"
|
||||
android:layout_weight="1"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/breeze"
|
||||
android:tint="@color/colour_five" />
|
||||
style="@style/icon_style__further_deatils" />
|
||||
</FrameLayout>
|
||||
<RelativeLayout
|
||||
android:layout_weight="2"
|
||||
android:layout_width="0dp"
|
||||
@@ -123,10 +122,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true">
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
@@ -137,14 +132,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="Wind: " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/windtext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="7mph"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
@@ -155,14 +150,16 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="12dp"
|
||||
android:layout_weight="1"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/water_drop"
|
||||
android:tint="@color/colour_five" />
|
||||
style="@style/icon_style__further_deatils" />
|
||||
</FrameLayout>
|
||||
<RelativeLayout
|
||||
android:layout_weight="2"
|
||||
android:layout_width="0dp"
|
||||
@@ -171,10 +168,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true">
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
@@ -189,14 +182,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="Humidity: " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/humiditytext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="85%"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
@@ -207,14 +200,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="Precipitation: " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/preciptext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="11mm"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
@@ -229,14 +222,16 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="12dp"
|
||||
android:layout_weight="1"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/sunrise"
|
||||
android:tint="@color/colour_five" />
|
||||
style="@style/icon_style__further_deatils" />
|
||||
</FrameLayout>
|
||||
<RelativeLayout
|
||||
android:layout_weight="2"
|
||||
android:layout_width="0dp"
|
||||
@@ -245,10 +240,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true">
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
@@ -263,14 +254,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="UV: " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/uvtext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="7"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
@@ -282,14 +273,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="Sunrise:" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sunrisetext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="05:30am"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
@@ -300,14 +291,14 @@
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="2"
|
||||
android:text="Sunset:" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sunsettext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="3"
|
||||
android:text="06:12pm"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="com.appttude.h_mal.atlas_weather.MainActivity">
|
||||
tools:context=".legacy.ui.home.MainActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
@@ -24,23 +25,29 @@
|
||||
tools:fontfamily="@font/archeologicaps"
|
||||
tools:title="Atlas Weather">
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<android.support.design.widget.TabLayout
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:tab="hello"/>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" >
|
||||
|
||||
</android.support.v4.view.ViewPager>
|
||||
</androidx.viewpager.widget.ViewPager>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_circular"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
|
||||
58
app/src/main/res/layout/activity_main_navigation.xml
Normal file
58
app/src/main/res/layout/activity_main_navigation.xml
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@android:color/transparent"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"
|
||||
tools:title="Atlas Weather"/>
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/nav_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
android:background="?android:attr/windowBackground"
|
||||
app:itemIconTint="@android:color/background_light"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:menu="@menu/tabs_menu" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/container"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/nav_view"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_circular"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="0.2dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="1dp">
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="@android:color/transparent"
|
||||
app:cardCornerRadius="22dp"
|
||||
card_view:cardElevation="1dp"
|
||||
card_view:cardMaxElevation="1dp"
|
||||
card_view:cardPreventCornerOverlap="false"
|
||||
card_view:cardUseCompatPadding="true">
|
||||
|
||||
@@ -91,5 +92,5 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</RelativeLayout>
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.appttude.h_mal.atlas_weather.Fragment_Two">
|
||||
tools:context=".legacy.ui.home.FragmentTwo">
|
||||
|
||||
<ListView
|
||||
android:layout_width="match_parent"
|
||||
@@ -12,43 +12,7 @@
|
||||
android:divider="@null">
|
||||
</ListView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/emptyView">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitCenter"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_gravity="center"
|
||||
android:alpha="0.8"
|
||||
android:src="@drawable/triple"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="List Empty"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/emptyViewText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="Add locations" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/floatingActionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
46
app/src/main/res/layout/fragment_add_location.xml
Normal file
46
app/src/main/res/layout/fragment_add_location.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".legacy.ui.home.FragmentTwo">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:id="@+id/world_recycler"
|
||||
android:divider="@null">
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/floatingActionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:layout_marginBottom="36dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:clickable="true"
|
||||
app:srcCompat="@android:drawable/ic_input_add"
|
||||
android:tint="@android:color/white"
|
||||
app:elevation="0dp"
|
||||
android:focusable="true" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar2"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
31
app/src/main/res/layout/fragment_home.xml
Normal file
31
app/src/main/res/layout/fragment_home.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".legacy.ui.home.MainActivity">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/swipe_refresh">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/forecast_listview">
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -1,21 +1,20 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.appttude.h_mal.atlas_weather.MainActivity">
|
||||
tools:context=".legacy.ui.home.MainActivity">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/swipe_refresh">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/forecast_listview">
|
||||
</android.support.v7.widget.RecyclerView>
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
|
||||
<ProgressBar
|
||||
@@ -51,7 +50,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:id="@id/emptyViewText"
|
||||
android:id="@+id/emptyViewText"
|
||||
android:text="check connection or location settings" />
|
||||
|
||||
<Button
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user