First commit
14
.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
164
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<DBN-PSQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false" />
|
||||||
|
</DBN-PSQL>
|
||||||
|
<DBN-SQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false">
|
||||||
|
<option name="STATEMENT_SPACING" value="one_line" />
|
||||||
|
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||||
|
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||||
|
</formatting-settings>
|
||||||
|
</DBN-SQL>
|
||||||
|
<DBN-PSQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false" />
|
||||||
|
</DBN-PSQL>
|
||||||
|
<DBN-SQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false">
|
||||||
|
<option name="STATEMENT_SPACING" value="one_line" />
|
||||||
|
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||||
|
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||||
|
</formatting-settings>
|
||||||
|
</DBN-SQL>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
|
<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>
|
454
.idea/dbnavigator.xml
generated
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DBNavigator.Project.DataEditorManager">
|
||||||
|
<record-view-column-sorting-type value="BY_INDEX" />
|
||||||
|
<value-preview-text-wrapping value="true" />
|
||||||
|
<value-preview-pinned value="false" />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DataExportManager">
|
||||||
|
<export-instructions>
|
||||||
|
<create-header value="true" />
|
||||||
|
<quote-values-containing-separator value="true" />
|
||||||
|
<quote-all-values value="false" />
|
||||||
|
<value-separator value="" />
|
||||||
|
<file-name value="" />
|
||||||
|
<file-location value="" />
|
||||||
|
<scope value="GLOBAL" />
|
||||||
|
<destination value="FILE" />
|
||||||
|
<format value="EXCEL" />
|
||||||
|
<charset value="windows-31j" />
|
||||||
|
</export-instructions>
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DatabaseBrowserManager">
|
||||||
|
<autoscroll-to-editor value="false" />
|
||||||
|
<autoscroll-from-editor value="true" />
|
||||||
|
<show-object-properties value="true" />
|
||||||
|
<loaded-nodes />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DatabaseFileManager">
|
||||||
|
<open-files />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.EditorStateManager">
|
||||||
|
<last-used-providers />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.MethodExecutionManager">
|
||||||
|
<method-browser />
|
||||||
|
<execution-history>
|
||||||
|
<group-entries value="true" />
|
||||||
|
<execution-inputs />
|
||||||
|
</execution-history>
|
||||||
|
<argument-values-cache />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.ObjectDependencyManager">
|
||||||
|
<last-used-dependency-type value="INCOMING" />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.ObjectQuickFilterManager">
|
||||||
|
<last-used-operator value="EQUAL" />
|
||||||
|
<filters />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.ScriptExecutionManager" clear-outputs="true">
|
||||||
|
<recently-used-interfaces />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.Settings">
|
||||||
|
<connections />
|
||||||
|
<browser-settings>
|
||||||
|
<general>
|
||||||
|
<display-mode value="TABBED" />
|
||||||
|
<navigation-history-size value="100" />
|
||||||
|
<show-object-details value="false" />
|
||||||
|
</general>
|
||||||
|
<filters>
|
||||||
|
<object-type-filter>
|
||||||
|
<object-type name="SCHEMA" enabled="true" />
|
||||||
|
<object-type name="USER" enabled="true" />
|
||||||
|
<object-type name="ROLE" enabled="true" />
|
||||||
|
<object-type name="PRIVILEGE" enabled="true" />
|
||||||
|
<object-type name="CHARSET" enabled="true" />
|
||||||
|
<object-type name="TABLE" enabled="true" />
|
||||||
|
<object-type name="VIEW" enabled="true" />
|
||||||
|
<object-type name="MATERIALIZED_VIEW" enabled="true" />
|
||||||
|
<object-type name="NESTED_TABLE" enabled="true" />
|
||||||
|
<object-type name="COLUMN" enabled="true" />
|
||||||
|
<object-type name="INDEX" enabled="true" />
|
||||||
|
<object-type name="CONSTRAINT" enabled="true" />
|
||||||
|
<object-type name="DATASET_TRIGGER" enabled="true" />
|
||||||
|
<object-type name="DATABASE_TRIGGER" enabled="true" />
|
||||||
|
<object-type name="SYNONYM" enabled="true" />
|
||||||
|
<object-type name="SEQUENCE" enabled="true" />
|
||||||
|
<object-type name="PROCEDURE" enabled="true" />
|
||||||
|
<object-type name="FUNCTION" enabled="true" />
|
||||||
|
<object-type name="PACKAGE" enabled="true" />
|
||||||
|
<object-type name="TYPE" enabled="true" />
|
||||||
|
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
|
||||||
|
<object-type name="ARGUMENT" enabled="true" />
|
||||||
|
<object-type name="DIMENSION" enabled="true" />
|
||||||
|
<object-type name="CLUSTER" enabled="true" />
|
||||||
|
<object-type name="DBLINK" enabled="true" />
|
||||||
|
</object-type-filter>
|
||||||
|
</filters>
|
||||||
|
<sorting>
|
||||||
|
<object-type name="COLUMN" sorting-type="NAME" />
|
||||||
|
<object-type name="FUNCTION" sorting-type="NAME" />
|
||||||
|
<object-type name="PROCEDURE" sorting-type="NAME" />
|
||||||
|
<object-type name="ARGUMENT" sorting-type="POSITION" />
|
||||||
|
</sorting>
|
||||||
|
<default-editors>
|
||||||
|
<object-type name="VIEW" editor-type="SELECTION" />
|
||||||
|
<object-type name="PACKAGE" editor-type="SELECTION" />
|
||||||
|
<object-type name="TYPE" editor-type="SELECTION" />
|
||||||
|
</default-editors>
|
||||||
|
</browser-settings>
|
||||||
|
<navigation-settings>
|
||||||
|
<lookup-filters>
|
||||||
|
<lookup-objects>
|
||||||
|
<object-type name="SCHEMA" enabled="true" />
|
||||||
|
<object-type name="USER" enabled="false" />
|
||||||
|
<object-type name="ROLE" enabled="false" />
|
||||||
|
<object-type name="PRIVILEGE" enabled="false" />
|
||||||
|
<object-type name="CHARSET" enabled="false" />
|
||||||
|
<object-type name="TABLE" enabled="true" />
|
||||||
|
<object-type name="VIEW" enabled="true" />
|
||||||
|
<object-type name="MATERIALIZED VIEW" enabled="true" />
|
||||||
|
<object-type name="NESTED TABLE" enabled="false" />
|
||||||
|
<object-type name="COLUMN" enabled="false" />
|
||||||
|
<object-type name="INDEX" enabled="true" />
|
||||||
|
<object-type name="CONSTRAINT" enabled="true" />
|
||||||
|
<object-type name="DATASET TRIGGER" enabled="true" />
|
||||||
|
<object-type name="DATABASE TRIGGER" enabled="true" />
|
||||||
|
<object-type name="SYNONYM" enabled="false" />
|
||||||
|
<object-type name="SEQUENCE" enabled="true" />
|
||||||
|
<object-type name="PROCEDURE" enabled="true" />
|
||||||
|
<object-type name="FUNCTION" enabled="true" />
|
||||||
|
<object-type name="PACKAGE" enabled="true" />
|
||||||
|
<object-type name="TYPE" enabled="true" />
|
||||||
|
<object-type name="TYPE ATTRIBUTE" enabled="false" />
|
||||||
|
<object-type name="ARGUMENT" enabled="false" />
|
||||||
|
<object-type name="DIMENSION" enabled="false" />
|
||||||
|
<object-type name="CLUSTER" enabled="false" />
|
||||||
|
<object-type name="DBLINK" enabled="true" />
|
||||||
|
</lookup-objects>
|
||||||
|
<force-database-load value="false" />
|
||||||
|
<prompt-connection-selection value="true" />
|
||||||
|
<prompt-schema-selection value="true" />
|
||||||
|
</lookup-filters>
|
||||||
|
</navigation-settings>
|
||||||
|
<dataset-grid-settings>
|
||||||
|
<general>
|
||||||
|
<enable-zooming value="true" />
|
||||||
|
<enable-column-tooltip value="true" />
|
||||||
|
</general>
|
||||||
|
<sorting>
|
||||||
|
<nulls-first value="true" />
|
||||||
|
<max-sorting-columns value="4" />
|
||||||
|
</sorting>
|
||||||
|
<tracking-columns>
|
||||||
|
<columnNames value="" />
|
||||||
|
<visible value="true" />
|
||||||
|
<editable value="false" />
|
||||||
|
</tracking-columns>
|
||||||
|
</dataset-grid-settings>
|
||||||
|
<dataset-editor-settings>
|
||||||
|
<text-editor-popup>
|
||||||
|
<active value="false" />
|
||||||
|
<active-if-empty value="false" />
|
||||||
|
<data-length-threshold value="100" />
|
||||||
|
<popup-delay value="1000" />
|
||||||
|
</text-editor-popup>
|
||||||
|
<values-actions-popup>
|
||||||
|
<show-popup-button value="true" />
|
||||||
|
<element-count-threshold value="1000" />
|
||||||
|
<data-length-threshold value="250" />
|
||||||
|
</values-actions-popup>
|
||||||
|
<general>
|
||||||
|
<fetch-block-size value="100" />
|
||||||
|
<fetch-timeout value="30" />
|
||||||
|
<trim-whitespaces value="true" />
|
||||||
|
<convert-empty-strings-to-null value="true" />
|
||||||
|
<select-content-on-cell-edit value="true" />
|
||||||
|
<large-value-preview-active value="true" />
|
||||||
|
</general>
|
||||||
|
<filters>
|
||||||
|
<prompt-filter-dialog value="true" />
|
||||||
|
<default-filter-type value="BASIC" />
|
||||||
|
</filters>
|
||||||
|
<qualified-text-editor text-length-threshold="300">
|
||||||
|
<content-types>
|
||||||
|
<content-type name="Text" enabled="true" />
|
||||||
|
<content-type name="Properties" enabled="true" />
|
||||||
|
<content-type name="XML" enabled="true" />
|
||||||
|
<content-type name="DTD" enabled="true" />
|
||||||
|
<content-type name="HTML" enabled="true" />
|
||||||
|
<content-type name="XHTML" enabled="true" />
|
||||||
|
<content-type name="Java" enabled="true" />
|
||||||
|
<content-type name="Groovy" enabled="true" />
|
||||||
|
<content-type name="AIDL" enabled="true" />
|
||||||
|
<content-type name="YAML" enabled="true" />
|
||||||
|
<content-type name="Manifest" enabled="true" />
|
||||||
|
</content-types>
|
||||||
|
</qualified-text-editor>
|
||||||
|
<record-navigation>
|
||||||
|
<navigation-target value="VIEWER" />
|
||||||
|
</record-navigation>
|
||||||
|
</dataset-editor-settings>
|
||||||
|
<code-editor-settings>
|
||||||
|
<general>
|
||||||
|
<show-object-navigation-gutter value="false" />
|
||||||
|
<show-spec-declaration-navigation-gutter value="true" />
|
||||||
|
<enable-spellchecking value="true" />
|
||||||
|
<enable-reference-spellchecking value="false" />
|
||||||
|
</general>
|
||||||
|
<confirmations>
|
||||||
|
<save-changes value="false" />
|
||||||
|
<revert-changes value="true" />
|
||||||
|
</confirmations>
|
||||||
|
</code-editor-settings>
|
||||||
|
<code-completion-settings>
|
||||||
|
<filters>
|
||||||
|
<basic-filter>
|
||||||
|
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
|
||||||
|
<filter-element type="RESERVED_WORD" id="function" selected="true" />
|
||||||
|
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
|
||||||
|
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
|
||||||
|
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="schema" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="role" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="user" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="privilege" selected="true" />
|
||||||
|
<user-schema>
|
||||||
|
<filter-element type="OBJECT" id="table" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="index" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="synonym" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="function" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="package" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="type" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||||
|
</user-schema>
|
||||||
|
<public-schema>
|
||||||
|
<filter-element type="OBJECT" id="table" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="view" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="materialized view" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="index" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="constraint" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="trigger" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="synonym" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="sequence" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="procedure" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="function" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="package" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="type" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="dimension" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="cluster" selected="false" />
|
||||||
|
<filter-element type="OBJECT" id="dblink" selected="false" />
|
||||||
|
</public-schema>
|
||||||
|
<any-schema>
|
||||||
|
<filter-element type="OBJECT" id="table" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="index" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="synonym" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="function" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="package" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="type" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||||
|
</any-schema>
|
||||||
|
</basic-filter>
|
||||||
|
<extended-filter>
|
||||||
|
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
|
||||||
|
<filter-element type="RESERVED_WORD" id="function" selected="true" />
|
||||||
|
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
|
||||||
|
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
|
||||||
|
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="schema" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="user" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="role" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="privilege" selected="true" />
|
||||||
|
<user-schema>
|
||||||
|
<filter-element type="OBJECT" id="table" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="index" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="synonym" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="function" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="package" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="type" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||||
|
</user-schema>
|
||||||
|
<public-schema>
|
||||||
|
<filter-element type="OBJECT" id="table" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="index" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="synonym" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="function" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="package" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="type" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||||
|
</public-schema>
|
||||||
|
<any-schema>
|
||||||
|
<filter-element type="OBJECT" id="table" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="index" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="synonym" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="function" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="package" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="type" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||||
|
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||||
|
</any-schema>
|
||||||
|
</extended-filter>
|
||||||
|
</filters>
|
||||||
|
<sorting enabled="true">
|
||||||
|
<sorting-element type="RESERVED_WORD" id="keyword" />
|
||||||
|
<sorting-element type="RESERVED_WORD" id="datatype" />
|
||||||
|
<sorting-element type="OBJECT" id="column" />
|
||||||
|
<sorting-element type="OBJECT" id="table" />
|
||||||
|
<sorting-element type="OBJECT" id="view" />
|
||||||
|
<sorting-element type="OBJECT" id="materialized view" />
|
||||||
|
<sorting-element type="OBJECT" id="index" />
|
||||||
|
<sorting-element type="OBJECT" id="constraint" />
|
||||||
|
<sorting-element type="OBJECT" id="trigger" />
|
||||||
|
<sorting-element type="OBJECT" id="synonym" />
|
||||||
|
<sorting-element type="OBJECT" id="sequence" />
|
||||||
|
<sorting-element type="OBJECT" id="procedure" />
|
||||||
|
<sorting-element type="OBJECT" id="function" />
|
||||||
|
<sorting-element type="OBJECT" id="package" />
|
||||||
|
<sorting-element type="OBJECT" id="type" />
|
||||||
|
<sorting-element type="OBJECT" id="dimension" />
|
||||||
|
<sorting-element type="OBJECT" id="cluster" />
|
||||||
|
<sorting-element type="OBJECT" id="dblink" />
|
||||||
|
<sorting-element type="OBJECT" id="schema" />
|
||||||
|
<sorting-element type="OBJECT" id="role" />
|
||||||
|
<sorting-element type="OBJECT" id="user" />
|
||||||
|
<sorting-element type="RESERVED_WORD" id="function" />
|
||||||
|
<sorting-element type="RESERVED_WORD" id="parameter" />
|
||||||
|
</sorting>
|
||||||
|
<format>
|
||||||
|
<enforce-code-style-case value="true" />
|
||||||
|
</format>
|
||||||
|
</code-completion-settings>
|
||||||
|
<execution-engine-settings>
|
||||||
|
<statement-execution>
|
||||||
|
<fetch-block-size value="100" />
|
||||||
|
<execution-timeout value="20" />
|
||||||
|
<debug-execution-timeout value="600" />
|
||||||
|
<focus-result value="false" />
|
||||||
|
<prompt-execution value="false" />
|
||||||
|
</statement-execution>
|
||||||
|
<script-execution>
|
||||||
|
<command-line-interfaces />
|
||||||
|
<execution-timeout value="300" />
|
||||||
|
</script-execution>
|
||||||
|
<method-execution>
|
||||||
|
<execution-timeout value="30" />
|
||||||
|
<debug-execution-timeout value="600" />
|
||||||
|
<parameter-history-size value="10" />
|
||||||
|
</method-execution>
|
||||||
|
</execution-engine-settings>
|
||||||
|
<operation-settings>
|
||||||
|
<transactions>
|
||||||
|
<uncommitted-changes>
|
||||||
|
<on-project-close value="ASK" />
|
||||||
|
<on-disconnect value="ASK" />
|
||||||
|
<on-autocommit-toggle value="ASK" />
|
||||||
|
</uncommitted-changes>
|
||||||
|
<multiple-uncommitted-changes>
|
||||||
|
<on-commit value="ASK" />
|
||||||
|
<on-rollback value="ASK" />
|
||||||
|
</multiple-uncommitted-changes>
|
||||||
|
</transactions>
|
||||||
|
<session-browser>
|
||||||
|
<disconnect-session value="ASK" />
|
||||||
|
<kill-session value="ASK" />
|
||||||
|
<reload-on-filter-change value="false" />
|
||||||
|
</session-browser>
|
||||||
|
<compiler>
|
||||||
|
<compile-type value="KEEP" />
|
||||||
|
<compile-dependencies value="ASK" />
|
||||||
|
<always-show-controls value="false" />
|
||||||
|
</compiler>
|
||||||
|
<debugger>
|
||||||
|
<debugger-type value="ASK" />
|
||||||
|
<use-generic-runners value="true" />
|
||||||
|
</debugger>
|
||||||
|
</operation-settings>
|
||||||
|
<ddl-file-settings>
|
||||||
|
<extensions>
|
||||||
|
<mapping file-type-id="VIEW" extensions="vw" />
|
||||||
|
<mapping file-type-id="TRIGGER" extensions="trg" />
|
||||||
|
<mapping file-type-id="PROCEDURE" extensions="prc" />
|
||||||
|
<mapping file-type-id="FUNCTION" extensions="fnc" />
|
||||||
|
<mapping file-type-id="PACKAGE" extensions="pkg" />
|
||||||
|
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
|
||||||
|
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
|
||||||
|
<mapping file-type-id="TYPE" extensions="tpe" />
|
||||||
|
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
|
||||||
|
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
|
||||||
|
</extensions>
|
||||||
|
<general>
|
||||||
|
<lookup-ddl-files value="true" />
|
||||||
|
<create-ddl-files value="false" />
|
||||||
|
<synchronize-ddl-files value="true" />
|
||||||
|
<use-qualified-names value="false" />
|
||||||
|
<make-scripts-rerunnable value="true" />
|
||||||
|
</general>
|
||||||
|
</ddl-file-settings>
|
||||||
|
<general-settings>
|
||||||
|
<regional-settings>
|
||||||
|
<date-format value="MEDIUM" />
|
||||||
|
<number-format value="UNGROUPED" />
|
||||||
|
<locale value="SYSTEM_DEFAULT" />
|
||||||
|
<use-custom-formats value="false" />
|
||||||
|
</regional-settings>
|
||||||
|
<environment>
|
||||||
|
<environment-types>
|
||||||
|
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
|
||||||
|
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
|
||||||
|
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
|
||||||
|
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
|
||||||
|
</environment-types>
|
||||||
|
<visibility-settings>
|
||||||
|
<connection-tabs value="true" />
|
||||||
|
<dialog-headers value="true" />
|
||||||
|
<object-editor-tabs value="true" />
|
||||||
|
<script-editor-tabs value="false" />
|
||||||
|
<execution-result-tabs value="true" />
|
||||||
|
</visibility-settings>
|
||||||
|
</environment>
|
||||||
|
</general-settings>
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.StatementExecutionManager">
|
||||||
|
<execution-variables />
|
||||||
|
</component>
|
||||||
|
</project>
|
16
.idea/gradle.xml
generated
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<compositeConfiguration>
|
||||||
|
<compositeBuild compositeDefinitionSource="SCRIPT" />
|
||||||
|
</compositeConfiguration>
|
||||||
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
<option name="testRunner" value="PLATFORM" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
9
.idea/misc.xml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
12
.idea/runConfigurations.xml
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
61
app/build.gradle
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
testOptions {
|
||||||
|
unitTests.returnDefaultValues = true
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
compileSdkVersion 29
|
||||||
|
buildToolsVersion "29.0.2"
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.bernd32.weatherdemo"
|
||||||
|
minSdkVersion 24
|
||||||
|
targetSdkVersion 29
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
|
|
||||||
|
implementation 'com.google.android.material:material:1.2.0-alpha03'
|
||||||
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
|
implementation 'com.squareup.retrofit2:retrofit:2.7.0'
|
||||||
|
implementation 'com.squareup.retrofit2:converter-gson:2.7.0'
|
||||||
|
implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0"
|
||||||
|
implementation "io.reactivex.rxjava2:rxandroid:2.1.0"
|
||||||
|
implementation "androidx.room:room-rxjava2:2.2.3"
|
||||||
|
// GSON body parser
|
||||||
|
implementation 'com.google.code.gson:gson:2.8.5'
|
||||||
|
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
|
||||||
|
// RecyclerView
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
|
// Glide
|
||||||
|
implementation 'com.github.bumptech.glide:glide:4.10.0'
|
||||||
|
annotationProcessor 'com.github.bumptech.glide:glide:4.10.0'
|
||||||
|
implementation 'com.google.android.gms:play-services-location:17.0.0'
|
||||||
|
implementation 'com.facebook.stetho:stetho:1.5.1'
|
||||||
|
// Room components
|
||||||
|
implementation "androidx.room:room-runtime:$rootProject.roomVersion"
|
||||||
|
annotationProcessor "androidx.room:room-compiler:$rootProject.roomVersion"
|
||||||
|
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
|
||||||
|
// debugImplementation because LeakCanary should only run in debug builds.
|
||||||
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.1'
|
||||||
|
}
|
21
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.bernd32.weatherdemo;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.ext.junit.runners.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() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
|
||||||
|
assertEquals("com.bernd32.weatherdemo", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
31
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.bernd32.weatherdemo">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
<activity android:name=".ui.AddNewLocation"></activity>
|
||||||
|
<activity android:name=".ui.MainActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="preloaded_fonts"
|
||||||
|
android:resource="@array/preloaded_fonts" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
BIN
app/src/main/ic_launcher-web.png
Normal file
After Width: | Height: | Size: 15 KiB |
10
app/src/main/java/com/bernd32/weatherdemo/Constants.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.bernd32.weatherdemo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants for our API
|
||||||
|
*/
|
||||||
|
public class Constants {
|
||||||
|
public static final String BASE_URL = "http://api.openweathermap.org";
|
||||||
|
public static final String API_KEY = "8296eaaa0ef012da8f776aa0ec890817";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.bernd32.weatherdemo;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.models.forecastdata.ForecastData;
|
||||||
|
import com.bernd32.weatherdemo.models.weatherdata.WeatherData;
|
||||||
|
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here we define the Retrofit endpoints with Observable return types to use it later
|
||||||
|
* in RxJava.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface EndpointInterface {
|
||||||
|
|
||||||
|
@GET("data/2.5/weather")
|
||||||
|
Observable<WeatherData> getCurrentConditions(@Query("units") String units,
|
||||||
|
@Query("APPID") String apiKey,
|
||||||
|
@Query("q") String city,
|
||||||
|
@Query("lang") String lang,
|
||||||
|
@Query("lat") String lat,
|
||||||
|
@Query("lon") String lon);
|
||||||
|
|
||||||
|
@GET("data/2.5/forecast")
|
||||||
|
Observable<ForecastData> getForecast(@Query("units") String units,
|
||||||
|
@Query("APPID") String apiKey,
|
||||||
|
@Query("q") String city,
|
||||||
|
@Query("lang") String lang,
|
||||||
|
@Query("lat") String lat,
|
||||||
|
@Query("lon") String lon);
|
||||||
|
}
|
41
app/src/main/java/com/bernd32/weatherdemo/LocationsDao.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package com.bernd32.weatherdemo;
|
||||||
|
|
||||||
|
import androidx.room.Dao;
|
||||||
|
import androidx.room.Delete;
|
||||||
|
import androidx.room.Insert;
|
||||||
|
import androidx.room.OnConflictStrategy;
|
||||||
|
import androidx.room.Query;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.models.UserLocation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.Flowable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used for accessing the database by defining DAO methods
|
||||||
|
* https://developer.android.com/training/data-storage/room/accessing-data
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
public interface LocationsDao {
|
||||||
|
|
||||||
|
// Notifies its active observers when the data has changed.
|
||||||
|
@Query("SELECT * FROM locations")
|
||||||
|
Flowable<List<UserLocation>> getAllLocations();
|
||||||
|
|
||||||
|
@Query("SELECT * from locations ORDER BY id DESC")
|
||||||
|
Flowable<List<UserLocation>> getLocationsById();
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
void insert(UserLocation location);
|
||||||
|
|
||||||
|
@Query("DELETE FROM locations")
|
||||||
|
void deleteAll();
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
void deleteLocation(UserLocation userLocation);
|
||||||
|
|
||||||
|
@Query("DELETE FROM locations WHERE id = :id")
|
||||||
|
void deleteLocationById(int id);
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.bernd32.weatherdemo;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.room.Database;
|
||||||
|
import androidx.room.Room;
|
||||||
|
import androidx.room.RoomDatabase;
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.models.UserLocation;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
@Database(entities = {UserLocation.class}, version = 2, exportSchema = false)
|
||||||
|
public abstract class LocationsRoomDatabase extends RoomDatabase {
|
||||||
|
|
||||||
|
public abstract LocationsDao locationsDao();
|
||||||
|
|
||||||
|
// marking the instance as volatile to ensure atomic access to the variable
|
||||||
|
private static volatile LocationsRoomDatabase INSTANCE;
|
||||||
|
private static final int NUMBER_OF_THREADS = 4;
|
||||||
|
public static final ExecutorService databaseWriteExecutor =
|
||||||
|
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
|
||||||
|
|
||||||
|
public static LocationsRoomDatabase getDatabase(final Context context) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
synchronized (LocationsRoomDatabase.class) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
|
||||||
|
LocationsRoomDatabase.class, "locations_database")
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.addCallback(sRoomDatabaseCallback)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the onOpen method to populate the database.
|
||||||
|
* For this sample, we clear the database every time it is created or opened.
|
||||||
|
*
|
||||||
|
* If you want to populate the database only when the database is created for the 1st time,
|
||||||
|
* override RoomDatabase.Callback()#onCreate
|
||||||
|
*/
|
||||||
|
private static Callback sRoomDatabaseCallback = new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onOpen(@NonNull SupportSQLiteDatabase db) {
|
||||||
|
super.onOpen(db);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.bernd32.weatherdemo;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
|
|
||||||
|
import static com.bernd32.weatherdemo.Constants.BASE_URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class supplies an instance of Retrofit adapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RetrofitAdapter {
|
||||||
|
|
||||||
|
private static Retrofit retrofit;
|
||||||
|
private static Gson gson;
|
||||||
|
|
||||||
|
public static Retrofit getInstance() {
|
||||||
|
|
||||||
|
if (retrofit == null) {
|
||||||
|
if (gson == null) {
|
||||||
|
gson = new GsonBuilder().setLenient().create();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
|
||||||
|
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
|
||||||
|
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||||
|
.addInterceptor(logging)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
retrofit = new Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||||
|
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
|
||||||
|
.client(okHttpClient)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return retrofit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.bernd32.weatherdemo.models;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.room.ColumnInfo;
|
||||||
|
import androidx.room.Entity;
|
||||||
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for Room
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Entity(tableName = "locations")
|
||||||
|
public class UserLocation {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@ColumnInfo(name = "city")
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public UserLocation(@NonNull String city) {
|
||||||
|
this.city = city;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getCity() {
|
||||||
|
return city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCity(String city) {
|
||||||
|
this.city = city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.bernd32.weatherdemo.models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for RecyclerView
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class WeatherItem {
|
||||||
|
|
||||||
|
private int mImgResource;
|
||||||
|
private String mTitle;
|
||||||
|
private String mValue;
|
||||||
|
private String imgUrl;
|
||||||
|
|
||||||
|
public WeatherItem(int mImgResource, String mTitle, String mValue) {
|
||||||
|
this.mImgResource = mImgResource;
|
||||||
|
this.mTitle = mTitle;
|
||||||
|
this.mValue = mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WeatherItem(String imgUrl, String mTitle, String mValue) {
|
||||||
|
this.imgUrl = imgUrl;
|
||||||
|
this.mTitle = mTitle;
|
||||||
|
this.mValue = mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImgUrl() {
|
||||||
|
return imgUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImgResource() {
|
||||||
|
return mImgResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return mTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return mValue;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class City {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
@Expose
|
||||||
|
private Integer id;
|
||||||
|
@SerializedName("name")
|
||||||
|
@Expose
|
||||||
|
private String name;
|
||||||
|
@SerializedName("coord")
|
||||||
|
@Expose
|
||||||
|
private Coord coord;
|
||||||
|
@SerializedName("country")
|
||||||
|
@Expose
|
||||||
|
private String country;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coord getCoord() {
|
||||||
|
return coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCoord(Coord coord) {
|
||||||
|
this.coord = coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCountry() {
|
||||||
|
return country;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCountry(String country) {
|
||||||
|
this.country = country;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Clouds {
|
||||||
|
|
||||||
|
@SerializedName("all")
|
||||||
|
@Expose
|
||||||
|
private Integer all;
|
||||||
|
|
||||||
|
public Integer getAll() {
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAll(Integer all) {
|
||||||
|
this.all = all;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Coord {
|
||||||
|
|
||||||
|
@SerializedName("lat")
|
||||||
|
@Expose
|
||||||
|
private Double lat;
|
||||||
|
@SerializedName("lon")
|
||||||
|
@Expose
|
||||||
|
private Double lon;
|
||||||
|
|
||||||
|
public Double getLat() {
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLat(Double lat) {
|
||||||
|
this.lat = lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getLon() {
|
||||||
|
return lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLon(Double lon) {
|
||||||
|
this.lon = lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class ForecastData {
|
||||||
|
|
||||||
|
@SerializedName("cod")
|
||||||
|
@Expose
|
||||||
|
private String cod;
|
||||||
|
@SerializedName("message")
|
||||||
|
@Expose
|
||||||
|
private Double message;
|
||||||
|
@SerializedName("cnt")
|
||||||
|
@Expose
|
||||||
|
private Integer cnt;
|
||||||
|
@SerializedName("list")
|
||||||
|
@Expose
|
||||||
|
private java.util.List<com.bernd32.weatherdemo.models.forecastdata.List> list = null;
|
||||||
|
@SerializedName("city")
|
||||||
|
@Expose
|
||||||
|
private City city;
|
||||||
|
|
||||||
|
public String getCod() {
|
||||||
|
return cod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCod(String cod) {
|
||||||
|
this.cod = cod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(Double message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCnt() {
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCnt(Integer cnt) {
|
||||||
|
this.cnt = cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public java.util.List<com.bernd32.weatherdemo.models.forecastdata.List> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(java.util.List<com.bernd32.weatherdemo.models.forecastdata.List> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public City getCity() {
|
||||||
|
return city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCity(City city) {
|
||||||
|
this.city = city;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class List {
|
||||||
|
|
||||||
|
@SerializedName("dt")
|
||||||
|
@Expose
|
||||||
|
private Integer dt;
|
||||||
|
@SerializedName("main")
|
||||||
|
@Expose
|
||||||
|
private Main main;
|
||||||
|
@SerializedName("weather")
|
||||||
|
@Expose
|
||||||
|
private java.util.List<Weather> weather = null;
|
||||||
|
@SerializedName("clouds")
|
||||||
|
@Expose
|
||||||
|
private Clouds clouds;
|
||||||
|
@SerializedName("wind")
|
||||||
|
@Expose
|
||||||
|
private Wind wind;
|
||||||
|
@SerializedName("snow")
|
||||||
|
@Expose
|
||||||
|
private Snow snow;
|
||||||
|
@SerializedName("sys")
|
||||||
|
@Expose
|
||||||
|
private Sys sys;
|
||||||
|
@SerializedName("dt_txt")
|
||||||
|
@Expose
|
||||||
|
private String dtTxt;
|
||||||
|
|
||||||
|
public Integer getDt() {
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDt(Integer dt) {
|
||||||
|
this.dt = dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Main getMain() {
|
||||||
|
return main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMain(Main main) {
|
||||||
|
this.main = main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public java.util.List<Weather> getWeather() {
|
||||||
|
return weather;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWeather(java.util.List<Weather> weather) {
|
||||||
|
this.weather = weather;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Clouds getClouds() {
|
||||||
|
return clouds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClouds(Clouds clouds) {
|
||||||
|
this.clouds = clouds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Wind getWind() {
|
||||||
|
return wind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWind(Wind wind) {
|
||||||
|
this.wind = wind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Snow getSnow() {
|
||||||
|
return snow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnow(Snow snow) {
|
||||||
|
this.snow = snow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sys getSys() {
|
||||||
|
return sys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSys(Sys sys) {
|
||||||
|
this.sys = sys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDtTxt() {
|
||||||
|
return dtTxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDtTxt(String dtTxt) {
|
||||||
|
this.dtTxt = dtTxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
@SerializedName("temp")
|
||||||
|
@Expose
|
||||||
|
private Double temp;
|
||||||
|
@SerializedName("temp_min")
|
||||||
|
@Expose
|
||||||
|
private Double tempMin;
|
||||||
|
@SerializedName("temp_max")
|
||||||
|
@Expose
|
||||||
|
private Double tempMax;
|
||||||
|
@SerializedName("pressure")
|
||||||
|
@Expose
|
||||||
|
private Integer pressure;
|
||||||
|
@SerializedName("sea_level")
|
||||||
|
@Expose
|
||||||
|
private Double seaLevel;
|
||||||
|
@SerializedName("grnd_level")
|
||||||
|
@Expose
|
||||||
|
private Integer grndLevel;
|
||||||
|
@SerializedName("humidity")
|
||||||
|
@Expose
|
||||||
|
private Integer humidity;
|
||||||
|
@SerializedName("temp_kf")
|
||||||
|
@Expose
|
||||||
|
private Double tempKf;
|
||||||
|
|
||||||
|
public Double getTemp() {
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemp(Double temp) {
|
||||||
|
this.temp = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getTempMin() {
|
||||||
|
return tempMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTempMin(Double tempMin) {
|
||||||
|
this.tempMin = tempMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getTempMax() {
|
||||||
|
return tempMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTempMax(Double tempMax) {
|
||||||
|
this.tempMax = tempMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPressure() {
|
||||||
|
return pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPressure(Integer pressure) {
|
||||||
|
this.pressure = pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getSeaLevel() {
|
||||||
|
return seaLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeaLevel(Double seaLevel) {
|
||||||
|
this.seaLevel = seaLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getGrndLevel() {
|
||||||
|
return grndLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGrndLevel(Integer grndLevel) {
|
||||||
|
this.grndLevel = grndLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getHumidity() {
|
||||||
|
return humidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHumidity(Integer humidity) {
|
||||||
|
this.humidity = humidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getTempKf() {
|
||||||
|
return tempKf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Snow {
|
||||||
|
|
||||||
|
@SerializedName("3h")
|
||||||
|
@Expose
|
||||||
|
private Double _3h;
|
||||||
|
|
||||||
|
public Double get3h() {
|
||||||
|
return _3h;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set3h(Double _3h) {
|
||||||
|
this._3h = _3h;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Sys {
|
||||||
|
|
||||||
|
@SerializedName("pod")
|
||||||
|
@Expose
|
||||||
|
private String pod;
|
||||||
|
|
||||||
|
public String getPod() {
|
||||||
|
return pod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPod(String pod) {
|
||||||
|
this.pod = pod;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Weather {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
@Expose
|
||||||
|
private Integer id;
|
||||||
|
@SerializedName("main")
|
||||||
|
@Expose
|
||||||
|
private String main;
|
||||||
|
@SerializedName("description")
|
||||||
|
@Expose
|
||||||
|
private String description;
|
||||||
|
@SerializedName("icon")
|
||||||
|
@Expose
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMain() {
|
||||||
|
return main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMain(String main) {
|
||||||
|
this.main = main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.forecastdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Wind {
|
||||||
|
|
||||||
|
@SerializedName("speed")
|
||||||
|
@Expose
|
||||||
|
private Double speed;
|
||||||
|
@SerializedName("deg")
|
||||||
|
@Expose
|
||||||
|
private Double deg;
|
||||||
|
|
||||||
|
public Double getSpeed() {
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeed(Double speed) {
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getDeg() {
|
||||||
|
return deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeg(Double deg) {
|
||||||
|
this.deg = deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.weatherdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Clouds {
|
||||||
|
|
||||||
|
@SerializedName("all")
|
||||||
|
@Expose
|
||||||
|
private Integer all;
|
||||||
|
|
||||||
|
public Integer getAll() {
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAll(Integer all) {
|
||||||
|
this.all = all;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.weatherdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Coord {
|
||||||
|
|
||||||
|
@SerializedName("lon")
|
||||||
|
@Expose
|
||||||
|
private Double lon;
|
||||||
|
@SerializedName("lat")
|
||||||
|
@Expose
|
||||||
|
private Double lat;
|
||||||
|
|
||||||
|
public Double getLon() {
|
||||||
|
return lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLon(Double lon) {
|
||||||
|
this.lon = lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getLat() {
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLat(Double lat) {
|
||||||
|
this.lat = lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.weatherdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
@SerializedName("temp")
|
||||||
|
@Expose
|
||||||
|
private Double temp;
|
||||||
|
@SerializedName("feels_like")
|
||||||
|
@Expose
|
||||||
|
private Double feelsLike;
|
||||||
|
@SerializedName("temp_min")
|
||||||
|
@Expose
|
||||||
|
private Double tempMin;
|
||||||
|
@SerializedName("temp_max")
|
||||||
|
@Expose
|
||||||
|
private Double tempMax;
|
||||||
|
@SerializedName("pressure")
|
||||||
|
@Expose
|
||||||
|
private Integer pressure;
|
||||||
|
@SerializedName("humidity")
|
||||||
|
@Expose
|
||||||
|
private Integer humidity;
|
||||||
|
|
||||||
|
public Double getTemp() {
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemp(Double temp) {
|
||||||
|
this.temp = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getFeelsLike() {
|
||||||
|
return feelsLike;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFeelsLike(Double feelsLike) {
|
||||||
|
this.feelsLike = feelsLike;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getTempMin() {
|
||||||
|
return tempMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTempMin(Double tempMin) {
|
||||||
|
this.tempMin = tempMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getTempMax() {
|
||||||
|
return tempMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTempMax(Double tempMax) {
|
||||||
|
this.tempMax = tempMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPressure() {
|
||||||
|
return pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPressure(Integer pressure) {
|
||||||
|
this.pressure = pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getHumidity() {
|
||||||
|
return humidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHumidity(Integer humidity) {
|
||||||
|
this.humidity = humidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.weatherdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Sys {
|
||||||
|
|
||||||
|
@SerializedName("type")
|
||||||
|
@Expose
|
||||||
|
private Integer type;
|
||||||
|
@SerializedName("id")
|
||||||
|
@Expose
|
||||||
|
private Integer id;
|
||||||
|
@SerializedName("message")
|
||||||
|
@Expose
|
||||||
|
private Double message;
|
||||||
|
@SerializedName("country")
|
||||||
|
@Expose
|
||||||
|
private String country;
|
||||||
|
@SerializedName("sunrise")
|
||||||
|
@Expose
|
||||||
|
private Integer sunrise;
|
||||||
|
@SerializedName("sunset")
|
||||||
|
@Expose
|
||||||
|
private Integer sunset;
|
||||||
|
|
||||||
|
public Integer getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(Integer type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(Double message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCountry() {
|
||||||
|
return country;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCountry(String country) {
|
||||||
|
this.country = country;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSunrise() {
|
||||||
|
return sunrise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSunrise(Integer sunrise) {
|
||||||
|
this.sunrise = sunrise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSunset() {
|
||||||
|
return sunset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSunset(Integer sunset) {
|
||||||
|
this.sunset = sunset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.weatherdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Weather {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
@Expose
|
||||||
|
private Integer id;
|
||||||
|
@SerializedName("main")
|
||||||
|
@Expose
|
||||||
|
private String main;
|
||||||
|
@SerializedName("description")
|
||||||
|
@Expose
|
||||||
|
private String description;
|
||||||
|
@SerializedName("icon")
|
||||||
|
@Expose
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMain() {
|
||||||
|
return main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMain(String main) {
|
||||||
|
this.main = main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.weatherdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WeatherData {
|
||||||
|
|
||||||
|
@SerializedName("coord")
|
||||||
|
@Expose
|
||||||
|
private Coord coord;
|
||||||
|
@SerializedName("weather")
|
||||||
|
@Expose
|
||||||
|
private List<Weather> weather = null;
|
||||||
|
@SerializedName("base")
|
||||||
|
@Expose
|
||||||
|
private String base;
|
||||||
|
@SerializedName("main")
|
||||||
|
@Expose
|
||||||
|
private Main main;
|
||||||
|
@SerializedName("wind")
|
||||||
|
@Expose
|
||||||
|
private Wind wind;
|
||||||
|
@SerializedName("clouds")
|
||||||
|
@Expose
|
||||||
|
private Clouds clouds;
|
||||||
|
@SerializedName("dt")
|
||||||
|
@Expose
|
||||||
|
private Integer dt;
|
||||||
|
@SerializedName("sys")
|
||||||
|
@Expose
|
||||||
|
private Sys sys;
|
||||||
|
@SerializedName("timezone")
|
||||||
|
@Expose
|
||||||
|
private Integer timezone;
|
||||||
|
@SerializedName("id")
|
||||||
|
@Expose
|
||||||
|
private Integer id;
|
||||||
|
@SerializedName("name")
|
||||||
|
@Expose
|
||||||
|
private String name;
|
||||||
|
@SerializedName("cod")
|
||||||
|
@Expose
|
||||||
|
private Integer cod;
|
||||||
|
|
||||||
|
public Coord getCoord() {
|
||||||
|
return coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCoord(Coord coord) {
|
||||||
|
this.coord = coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Weather> getWeather() {
|
||||||
|
return weather;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWeather(List<Weather> weather) {
|
||||||
|
this.weather = weather;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase() {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBase(String base) {
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Main getMain() {
|
||||||
|
return main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMain(Main main) {
|
||||||
|
this.main = main;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Wind getWind() {
|
||||||
|
return wind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWind(Wind wind) {
|
||||||
|
this.wind = wind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Clouds getClouds() {
|
||||||
|
return clouds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClouds(Clouds clouds) {
|
||||||
|
this.clouds = clouds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getDt() {
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDt(Integer dt) {
|
||||||
|
this.dt = dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sys getSys() {
|
||||||
|
return sys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSys(Sys sys) {
|
||||||
|
this.sys = sys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTimezone() {
|
||||||
|
return timezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimezone(Integer timezone) {
|
||||||
|
this.timezone = timezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCod() {
|
||||||
|
return cod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCod(Integer cod) {
|
||||||
|
this.cod = cod;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
package com.bernd32.weatherdemo.models.weatherdata;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Wind {
|
||||||
|
|
||||||
|
@SerializedName("speed")
|
||||||
|
@Expose
|
||||||
|
private Double speed;
|
||||||
|
@SerializedName("deg")
|
||||||
|
@Expose
|
||||||
|
private Double deg;
|
||||||
|
|
||||||
|
public Double getSpeed() {
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeed(Double speed) {
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getDeg() {
|
||||||
|
return deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeg(Double deg) {
|
||||||
|
this.deg = deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
package com.bernd32.weatherdemo.presenter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.EndpointInterface;
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
import com.bernd32.weatherdemo.RetrofitAdapter;
|
||||||
|
import com.bernd32.weatherdemo.models.weatherdata.WeatherData;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import io.reactivex.Observer;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
|
||||||
|
import static com.bernd32.weatherdemo.Constants.API_KEY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here we get current conditions information by calling our weather API,
|
||||||
|
* and then update our UI via the callback interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class CurrentConditionsPresenter {
|
||||||
|
|
||||||
|
private View mView;
|
||||||
|
private Context mContext;
|
||||||
|
private static final String TAG = "CurrentConditionsPresenter";
|
||||||
|
|
||||||
|
public CurrentConditionsPresenter(View view, Context context) {
|
||||||
|
mView = view;
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getCurrentConditions(String latitude, String longitude, String city) {
|
||||||
|
Log.d(TAG, "getCurrentConditions: started");
|
||||||
|
Log.d(TAG, "getCurrentConditions: lat/long=" + latitude + "/"+ longitude);
|
||||||
|
Log.d(TAG, "getCurrentConditions: city=" + city);
|
||||||
|
Retrofit retrofit = RetrofitAdapter.getInstance();
|
||||||
|
EndpointInterface apiService = retrofit.create(EndpointInterface.class);
|
||||||
|
// Format latitude and longitude values
|
||||||
|
/* String formattedLat = new DecimalFormat("###.###").format(latitude);
|
||||||
|
String formattedLon = new DecimalFormat("###.###").format(longitude);*/
|
||||||
|
|
||||||
|
// Get the observable Weather object
|
||||||
|
apiService.getCurrentConditions("metric", API_KEY, city,
|
||||||
|
Locale.getDefault().getLanguage(), latitude, longitude)
|
||||||
|
.throttleFirst(10, TimeUnit.MINUTES)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.doOnSubscribe(__ -> mView.showProgressBar(true))
|
||||||
|
.doOnTerminate(() -> mView.showProgressBar(false))
|
||||||
|
.subscribe(new Observer<WeatherData>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(WeatherData weatherData) {
|
||||||
|
String location = String.format("%s, %s",
|
||||||
|
weatherData.getName(), weatherData.getSys().getCountry());
|
||||||
|
|
||||||
|
String url = String.format("http://openweathermap.org/img/wn/%s@2x.png",
|
||||||
|
weatherData.getWeather().get(0).getIcon());
|
||||||
|
|
||||||
|
String temperature = String.format(Locale.getDefault(), "%d%s",
|
||||||
|
Math.round(weatherData.getMain().getTemp()), "°C");
|
||||||
|
|
||||||
|
String pressure = String.format(Locale.getDefault(), "%d %s",
|
||||||
|
weatherData.getMain().getPressure(),
|
||||||
|
mContext.getString(R.string.pressure_unit));
|
||||||
|
|
||||||
|
String humidity = String.format(Locale.getDefault(), "%d %%",
|
||||||
|
weatherData.getMain().getHumidity());
|
||||||
|
|
||||||
|
String wind = String.format(Locale.getDefault(), "%.2f %s",
|
||||||
|
weatherData.getWind().getSpeed(),
|
||||||
|
mContext.getString(R.string.meters_per_second));
|
||||||
|
|
||||||
|
mView.updateTemperature(temperature);
|
||||||
|
mView.updateLocation(location);
|
||||||
|
mView.updateWeatherText(weatherData.getWeather().get(0).getDescription());
|
||||||
|
mView.updateIcon(url);
|
||||||
|
mView.updateDetails(pressure, wind, humidity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable t) {
|
||||||
|
Log.e(TAG, "onError: ", t.fillInStackTrace());
|
||||||
|
Log.e(TAG, "onError: ", t.getCause());
|
||||||
|
mView.showError(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
Log.d(TAG, "onComplete: ");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface View {
|
||||||
|
void updateTemperature(String temp);
|
||||||
|
void updateLocation(String location);
|
||||||
|
void updateWeatherText(String weatherText);
|
||||||
|
void updateIcon(String url);
|
||||||
|
void updateDetails(String pressure, String wind, String humidity);
|
||||||
|
void showProgressBar(boolean show);
|
||||||
|
void showError(Throwable t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,173 @@
|
|||||||
|
package com.bernd32.weatherdemo.presenter;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.EndpointInterface;
|
||||||
|
import com.bernd32.weatherdemo.RetrofitAdapter;
|
||||||
|
import com.bernd32.weatherdemo.models.forecastdata.ForecastData;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import io.reactivex.Observer;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
|
||||||
|
import static com.bernd32.weatherdemo.Constants.API_KEY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here we get current conditions information by calling our weather API,
|
||||||
|
* and then update our UI via the callback interface. We also performing
|
||||||
|
* a lot of formatting to make everything look clean :)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ForecastPresenter {
|
||||||
|
|
||||||
|
private static final String TAG = "ForecastPresenter";
|
||||||
|
private View mView;
|
||||||
|
private List<String> mDates = new ArrayList<>();
|
||||||
|
private List<Double> mMaxTempAll = new ArrayList<>();
|
||||||
|
private List<Double> mMinTempAll = new ArrayList<>();
|
||||||
|
private List<String> mDescriptions = new ArrayList<>();
|
||||||
|
private List<String> mImgUrls = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
public ForecastPresenter(View view) {
|
||||||
|
this.mView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getForecast(String latitude, String longitude, String city) {
|
||||||
|
Log.d(TAG, "getForecast: started");
|
||||||
|
Retrofit retrofit = RetrofitAdapter.getInstance();
|
||||||
|
EndpointInterface apiService = retrofit.create(EndpointInterface.class);
|
||||||
|
apiService.getForecast("metric", API_KEY, city,
|
||||||
|
Locale.getDefault().getLanguage(), latitude, longitude)
|
||||||
|
.throttleFirst(10, TimeUnit.MINUTES)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(new Observer<ForecastData>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(ForecastData forecastData) {
|
||||||
|
forecastDataHandle(forecastData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
Log.d(TAG, "onError: started");
|
||||||
|
Log.e(TAG, "onError: ", e.fillInStackTrace());
|
||||||
|
Log.e(TAG, "onError: ", e.getCause());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forecastDataHandle(ForecastData forecastData) {
|
||||||
|
// Populate array list with values emitted from rxjava
|
||||||
|
int size= forecastData.getList().size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
mMaxTempAll.add(forecastData.getList().get(i).getMain().getTempMax());
|
||||||
|
mMinTempAll.add(forecastData.getList().get(i).getMain().getTempMin());
|
||||||
|
mDates.add(formatUnixTime(forecastData.getList().get(i).getDt()));
|
||||||
|
mDescriptions.add(forecastData.getList().get(i).getWeather().get(0).getDescription());
|
||||||
|
mImgUrls.add(formatImgUrl(forecastData.getList().get(i).getWeather().get(0).getIcon()));
|
||||||
|
}
|
||||||
|
Log.d(TAG, "forecastDataHandle: mImgUrls = " + mImgUrls);
|
||||||
|
// We get 5 day / 3 hour forecast data, 40 items in total
|
||||||
|
// But we don't need such precise forecast, we just need
|
||||||
|
// to get 5 day / 1 day forecast, with min and max temps.
|
||||||
|
// But first we should calculate an arithmetic mean for the
|
||||||
|
// min/max temperature and add it to result array
|
||||||
|
final List<Double> maxTempAvg = getMaxAverage(mMaxTempAll, 8);
|
||||||
|
final List<Double> minTempAvg = getMinAverage(mMinTempAll, 8);
|
||||||
|
final List<String> avgMinMax = formatMinMaxValues(maxTempAvg, minTempAvg);
|
||||||
|
// Now we need to take every 8th element from mTemperatures, mDates, etc.
|
||||||
|
final List<String> dates = takeEveryNthElement(mDates, 8);
|
||||||
|
final List<String> descriptions = takeEveryNthElement(mDescriptions, 8);
|
||||||
|
final List<String> imgUrls = takeEveryNthElement(mImgUrls, 8);
|
||||||
|
// Send all of this to the UI
|
||||||
|
mView.updateForecast(avgMinMax, dates, descriptions, imgUrls);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> formatMinMaxValues(List<Double> maxTempAvg, List<Double> minTempAvg) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
for (int i = 0; i<maxTempAvg.size(); i++) {
|
||||||
|
long max = Math.round(maxTempAvg.get(i));
|
||||||
|
long min = Math.round(minTempAvg.get(i));
|
||||||
|
String fString = max + " .. " + min;
|
||||||
|
result.add(fString);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Double> getMinAverage(List<Double> list, int offset) {
|
||||||
|
List<Double> temp = new ArrayList<>();
|
||||||
|
for (int i = 0; i < list.size(); i += offset) {
|
||||||
|
int toIndex = i + offset;
|
||||||
|
if (toIndex > list.size()) break;
|
||||||
|
temp.add(findMin(list.subList(i, toIndex)));
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Double> getMaxAverage(List<Double> list, int offset) {
|
||||||
|
List<Double> temp = new ArrayList<>();
|
||||||
|
for (int i = 0; i < list.size(); i += offset) {
|
||||||
|
int toIndex = i + offset;
|
||||||
|
if (toIndex > list.size()) break;
|
||||||
|
temp.add(findMax(list.subList(i, toIndex)));
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> takeEveryNthElement(List<String> list, int nth) {
|
||||||
|
return IntStream.range(0, list.size())
|
||||||
|
.filter(n -> n % nth == 0)
|
||||||
|
.mapToObj(list::get)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private double findMin(List<Double> subList) {
|
||||||
|
return subList.stream().mapToDouble(val -> val).min().orElse(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double findMax(List<Double> subList) {
|
||||||
|
return subList.stream().mapToDouble(val -> val).max().orElse(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatImgUrl(String iconCode) {
|
||||||
|
return String.format("http://openweathermap.org/img/wn/%s@2x.png", iconCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatUnixTime(Integer unixSeconds) {
|
||||||
|
String[] locale = {Locale.getDefault().getLanguage(), Locale.getDefault().getCountry()};
|
||||||
|
Date date = new java.util.Date(unixSeconds*1000L);
|
||||||
|
SimpleDateFormat sdf = new java.text.SimpleDateFormat(
|
||||||
|
"EEEE dd MMMM",
|
||||||
|
new Locale(locale[0], locale[1]));
|
||||||
|
sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+5"));
|
||||||
|
return sdf.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface View {
|
||||||
|
void updateForecast(List<String> avgMinMax, List<String> mDates,
|
||||||
|
List<String> mDescriptions, List<String> mImgUrls);
|
||||||
|
}
|
||||||
|
}
|
223
app/src/main/java/com/bernd32/weatherdemo/ui/AddNewLocation.java
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
package com.bernd32.weatherdemo.ui;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.LocationsDao;
|
||||||
|
import com.bernd32.weatherdemo.LocationsRoomDatabase;
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
import com.bernd32.weatherdemo.models.UserLocation;
|
||||||
|
import com.bernd32.weatherdemo.ui.adapters.LocationsRecyclerAdapter;
|
||||||
|
import com.facebook.stetho.Stetho;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.Completable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.functions.Consumer;
|
||||||
|
import io.reactivex.observers.DisposableCompletableObserver;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In this activity user can add a new location manually or choose existing locations.
|
||||||
|
* Also here we load/save this data in a local database using Room
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class AddNewLocation extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final String TAG = "AddNewLocation";
|
||||||
|
private RecyclerView mRecyclerView;
|
||||||
|
private LocationsDao mLocationsDao;
|
||||||
|
private EditText cityEdit;
|
||||||
|
private CompositeDisposable mDisposable;
|
||||||
|
private LocationsRecyclerAdapter adapter;
|
||||||
|
private LocationsRoomDatabase db;
|
||||||
|
private Button addButton;
|
||||||
|
private TextView emptyMsg;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_add_new_location);
|
||||||
|
Stetho.initializeWithDefaults(this);
|
||||||
|
AddNewLocation.this.setTitle(getString(R.string.add_new_location));
|
||||||
|
cityEdit = findViewById(R.id.city_edit_text);
|
||||||
|
addButton = findViewById(R.id.button_add);
|
||||||
|
emptyMsg = findViewById(R.id.empty_message);
|
||||||
|
mRecyclerView = findViewById(R.id.recycler_view_locations);
|
||||||
|
mRecyclerView.setHasFixedSize(true);
|
||||||
|
adapter = new LocationsRecyclerAdapter(this, new ArrayList<>());
|
||||||
|
mRecyclerView.setAdapter(adapter);
|
||||||
|
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
|
||||||
|
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||||
|
|
||||||
|
db = LocationsRoomDatabase.getDatabase(this);
|
||||||
|
mLocationsDao = db.locationsDao();
|
||||||
|
mDisposable = new CompositeDisposable();
|
||||||
|
addButton.setEnabled(false);
|
||||||
|
cityEdit.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
if(s.toString().trim().length()==0){
|
||||||
|
addButton.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
addButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) { }
|
||||||
|
});
|
||||||
|
|
||||||
|
mDisposable.add(
|
||||||
|
mLocationsDao.getAllLocations()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(new Consumer<List<UserLocation>>() {
|
||||||
|
@Override
|
||||||
|
public void accept(List<UserLocation> userLocations) throws Exception {
|
||||||
|
adapter.addItems(userLocations);
|
||||||
|
// Show a message if the list is empty
|
||||||
|
emptyMsg.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
deleteItemBySwipe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteItemBySwipe() {
|
||||||
|
ItemTouchHelper helper = new ItemTouchHelper(
|
||||||
|
new ItemTouchHelper.SimpleCallback(0,
|
||||||
|
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
|
||||||
|
@Override
|
||||||
|
public boolean onMove(@NonNull RecyclerView recyclerView,
|
||||||
|
@NonNull RecyclerView.ViewHolder viewHolder,
|
||||||
|
@NonNull RecyclerView.ViewHolder target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// Delete an item from the database.
|
||||||
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
|
int position = viewHolder.getAdapterPosition();
|
||||||
|
UserLocation location = adapter.getItem(position);
|
||||||
|
Toast.makeText(AddNewLocation.this,
|
||||||
|
getString(R.string.delete_item_msg),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
deleteLocation(location);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Attach the item touch helper to the recycler view
|
||||||
|
helper.attachToRecyclerView(mRecyclerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteLocation(UserLocation location) {
|
||||||
|
Completable.fromAction(() -> {
|
||||||
|
mLocationsDao.deleteLocation(location);
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe(new DisposableCompletableObserver() {
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
Log.e(TAG, "onError: ", e.fillInStackTrace());
|
||||||
|
showInformationDialog(getString(R.string.error), e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
mDisposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.add_new_location_menu, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.delete_all) {
|
||||||
|
deleteAllItems();
|
||||||
|
Toast.makeText(this, getString(R.string.all_items_deleted_msg), Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteAllItems() {
|
||||||
|
Completable.fromAction(() -> {
|
||||||
|
mLocationsDao.deleteAll();
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe(new DisposableCompletableObserver() {
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
Log.e(TAG, "onError: ", e.fillInStackTrace());
|
||||||
|
showInformationDialog(getString(R.string.error), e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAddButton(View view) {
|
||||||
|
String city = cityEdit.getText().toString();
|
||||||
|
|
||||||
|
Completable.fromAction(() -> {
|
||||||
|
UserLocation userLocation = new UserLocation(city);
|
||||||
|
LocationsRoomDatabase.databaseWriteExecutor.execute(() -> {
|
||||||
|
mLocationsDao.insert(userLocation);
|
||||||
|
});
|
||||||
|
}).subscribe(new DisposableCompletableObserver() {
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
Log.d(TAG, "onComplete: done!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
Log.e(TAG, "onError: ", e.fillInStackTrace());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showInformationDialog(String title, String message) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(AddNewLocation.this);
|
||||||
|
builder.setTitle(title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setIcon(R.drawable.ic_info_outline_black_24dp)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setNegativeButton("OK", (dialog, id) -> dialog.cancel());
|
||||||
|
AlertDialog alert = builder.create();
|
||||||
|
alert.show();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
package com.bernd32.weatherdemo.ui;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
import com.bernd32.weatherdemo.models.WeatherItem;
|
||||||
|
import com.bernd32.weatherdemo.presenter.CurrentConditionsPresenter;
|
||||||
|
import com.bernd32.weatherdemo.ui.adapters.RecyclerAdapter;
|
||||||
|
import com.bernd32.weatherdemo.utils.PreferencesManager;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here we display current weather data by overriding callback interface methods
|
||||||
|
* sent by presenter.CurrentConditionsPresenter
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class CurrentWeatherFragment extends Fragment implements CurrentConditionsPresenter.View {
|
||||||
|
|
||||||
|
private static final String TAG = "CurrentWeatherFragment";
|
||||||
|
|
||||||
|
private TextView mCity, mTemp, mWeatherText;
|
||||||
|
private ImageView mIcon;
|
||||||
|
private RecyclerView mRecyclerView;
|
||||||
|
private ProgressBar mProgressBar;
|
||||||
|
private ArrayList<WeatherItem> mItems = new ArrayList<>();
|
||||||
|
private String mImgUrl;
|
||||||
|
|
||||||
|
public CurrentWeatherFragment() {
|
||||||
|
// Required empty public constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A new instance of fragment CurrentWeatherFragment.
|
||||||
|
*/
|
||||||
|
public static CurrentWeatherFragment newInstance() {
|
||||||
|
return new CurrentWeatherFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
Log.d(TAG, "onSaveInstanceState: start");
|
||||||
|
outState.putString("image_url", mImgUrl);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreate: started");
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setRetainInstance(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
getData();
|
||||||
|
} else {
|
||||||
|
updateIcon(mImgUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getData() {
|
||||||
|
// Read longitude and latitude values from PrefManager
|
||||||
|
PreferencesManager.initializeInstance(getContext());
|
||||||
|
PreferencesManager prefManager = PreferencesManager.getInstance();
|
||||||
|
String longitude = prefManager.getLongitude();
|
||||||
|
String latitude = prefManager.getLatitude();
|
||||||
|
String city = prefManager.getCity();
|
||||||
|
// Invoke the Presenter
|
||||||
|
CurrentConditionsPresenter conditionsPresenter = new CurrentConditionsPresenter(this, getContext());
|
||||||
|
conditionsPresenter.getCurrentConditions(latitude, longitude, city);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreateView: started");
|
||||||
|
// Inflate the layout for this fragment
|
||||||
|
View root = inflater.inflate(R.layout.fragment_current_weather, container, false);
|
||||||
|
mCity = root.findViewById(R.id.city_tv);
|
||||||
|
mTemp = root.findViewById(R.id.temperature_tv);
|
||||||
|
mWeatherText = root.findViewById(R.id.weatherText);
|
||||||
|
mIcon = root.findViewById(R.id.weatherIcon);
|
||||||
|
mProgressBar = root.findViewById(R.id.progressBar);
|
||||||
|
mRecyclerView = root.findViewById(R.id.recyclerView);
|
||||||
|
mRecyclerView.setHasFixedSize(true);
|
||||||
|
mRecyclerView.setAdapter(new RecyclerAdapter(getContext(), mItems));
|
||||||
|
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getContext());
|
||||||
|
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateTemperature(String temp) {
|
||||||
|
mTemp.setText(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateLocation(String location) {
|
||||||
|
mCity.setText(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateWeatherText(String weatherText) {
|
||||||
|
mWeatherText.setText(weatherText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateIcon(String url) {
|
||||||
|
mImgUrl = url;
|
||||||
|
Glide.with(this).load(mImgUrl).into(mIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateDetails(String pressure, String wind, String humidity) {
|
||||||
|
// Clear old information
|
||||||
|
mItems.clear();
|
||||||
|
|
||||||
|
mItems.add(new WeatherItem(R.drawable.ic_wi_barometer, getString(R.string.pressure), pressure));
|
||||||
|
mItems.add(new WeatherItem(R.drawable.ic_wi_day_windy, getString(R.string.wind), wind));
|
||||||
|
mItems.add(new WeatherItem(R.drawable.ic_wi_humidity, getString(R.string.humidity), humidity));
|
||||||
|
|
||||||
|
saveItems(mItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showProgressBar(boolean show) {
|
||||||
|
if (show) {
|
||||||
|
mProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
mProgressBar.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showError(Throwable t) {
|
||||||
|
String title = getString(R.string.error);
|
||||||
|
String message = t.getLocalizedMessage();
|
||||||
|
Activity act = getActivity();
|
||||||
|
if (act instanceof MainActivity) {
|
||||||
|
((MainActivity) act).showInformationDialog(title, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveItems(ArrayList<WeatherItem> mItems) {
|
||||||
|
RecyclerView.Adapter mAdapter = new RecyclerAdapter(getContext(), mItems);
|
||||||
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package com.bernd32.weatherdemo.ui;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
import com.bernd32.weatherdemo.models.WeatherItem;
|
||||||
|
import com.bernd32.weatherdemo.presenter.ForecastPresenter;
|
||||||
|
import com.bernd32.weatherdemo.ui.adapters.RecyclerAdapter;
|
||||||
|
import com.bernd32.weatherdemo.utils.PreferencesManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here we display forecast data by overriding callback interface methods
|
||||||
|
* sent by presenter.ForecastPresenter
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ForecastFragment extends Fragment implements ForecastPresenter.View{
|
||||||
|
|
||||||
|
private static final String TAG = "ForecastFragment";
|
||||||
|
private ArrayList<WeatherItem> mItems = new ArrayList<>();
|
||||||
|
private RecyclerView mRecyclerView;
|
||||||
|
|
||||||
|
public ForecastFragment() {
|
||||||
|
// Required empty public constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A new instance of fragment ForecastFragment.
|
||||||
|
*/
|
||||||
|
public static ForecastFragment newInstance() {
|
||||||
|
return new ForecastFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
Log.d(TAG, "onCreate: started");
|
||||||
|
setRetainInstance(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getData() {
|
||||||
|
// Read longitude and latitude values from PrefManager
|
||||||
|
PreferencesManager.initializeInstance(getContext());
|
||||||
|
PreferencesManager prefManager = PreferencesManager.getInstance();
|
||||||
|
String longitude = prefManager.getLongitude();
|
||||||
|
String latitude = prefManager.getLatitude();
|
||||||
|
String city = prefManager.getCity();
|
||||||
|
// Invoke the Presenter
|
||||||
|
ForecastPresenter forecastPresenter = new ForecastPresenter(this);
|
||||||
|
forecastPresenter.getForecast(latitude, longitude, city);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
// Inflate the layout for this fragment
|
||||||
|
View root = inflater.inflate(R.layout.fragment_forecast, container, false);
|
||||||
|
mRecyclerView = root.findViewById(R.id.recyclerView2);
|
||||||
|
mRecyclerView.setHasFixedSize(true);
|
||||||
|
mRecyclerView.setAdapter(new RecyclerAdapter(getContext(), mItems));
|
||||||
|
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getContext());
|
||||||
|
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveItems(ArrayList<WeatherItem> mItems) {
|
||||||
|
RecyclerView.Adapter mAdapter = new RecyclerAdapter(getContext(), mItems);
|
||||||
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateForecast(List<String> avgMinMax, List<String> mDates,
|
||||||
|
List<String> mDescriptions, List<String> mImgUrls) {
|
||||||
|
mItems.clear();
|
||||||
|
for (int i = 0; mDates.size() > i; i++) {
|
||||||
|
mItems.add(new WeatherItem(mImgUrls.get(i), mDates.get(i), avgMinMax.get(i)));
|
||||||
|
}
|
||||||
|
Log.d(TAG, "updateForecast: mImgUrls" + mImgUrls);
|
||||||
|
saveItems(mItems);
|
||||||
|
}
|
||||||
|
}
|
166
app/src/main/java/com/bernd32/weatherdemo/ui/MainActivity.java
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
package com.bernd32.weatherdemo.ui;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
import com.bernd32.weatherdemo.ui.adapters.TabsPagerAdapter;
|
||||||
|
import com.bernd32.weatherdemo.utils.LocationProvider;
|
||||||
|
import com.bernd32.weatherdemo.utils.PreferencesManager;
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
|
import static com.bernd32.weatherdemo.utils.LocationProvider.PERMISSION_ID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose what parameters (city name or location coordinates) we're using to call
|
||||||
|
* the weather API. Save params to PrefManager to use it across the app. Use
|
||||||
|
* utils.LocationProvider to get user's latitude and longitude if we're using
|
||||||
|
* location coordinates. Setup the UI (tabs, viewpager and fragments).
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity implements LocationProvider.Callback{
|
||||||
|
|
||||||
|
private static final String TAG = "MainActivity";
|
||||||
|
private PreferencesManager prefManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreate: started");
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
PreferencesManager.initializeInstance(this);
|
||||||
|
prefManager = PreferencesManager.getInstance();
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
prefManager.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
startApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startApplication() {
|
||||||
|
/*
|
||||||
|
If we have a city value in Intent then it means that user selected
|
||||||
|
a location from AddNewLocation activity. In this case we just initialize
|
||||||
|
the UI and use location name as a parameter
|
||||||
|
*/
|
||||||
|
if (getIntent().hasExtra("city")) {
|
||||||
|
prefManager.saveCity(getIntent().getStringExtra("city"));
|
||||||
|
initUI();
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
Otherwise, we request an user location data (latitude and longitude) and
|
||||||
|
use them as parameters. To achieve that we get user coordinates from the
|
||||||
|
LocationProvider and then return results via Callback interface by overriding
|
||||||
|
setResult() method.
|
||||||
|
*/
|
||||||
|
LocationProvider locationProvider = new LocationProvider(this, this);
|
||||||
|
locationProvider.getLastLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the app after we got permission result
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
if (requestCode == PERMISSION_ID) {// If request is cancelled, the result arrays are empty.
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(TAG, "onRequestPermissionsResult: granted");
|
||||||
|
// Permission was granted, yay!
|
||||||
|
startApplication();
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "onRequestPermissionsResult: denied");
|
||||||
|
// permission denied :( Open AddNewLocation activity where user
|
||||||
|
// can add a location for weather manually
|
||||||
|
startActivity(new Intent(this, AddNewLocation.class));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.main_menu, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
switch (item.getItemId()){
|
||||||
|
case R.id.open_saved_locations:
|
||||||
|
startActivity(new Intent(this, AddNewLocation.class));
|
||||||
|
return true;
|
||||||
|
case R.id.update:
|
||||||
|
startApplication();
|
||||||
|
Toast.makeText(this, getString(R.string.updated_msg), Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
case R.id.about_dialog:
|
||||||
|
showInformationDialog(
|
||||||
|
getString(R.string.about),
|
||||||
|
getString(R.string.about_message)
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResult(Location location) {
|
||||||
|
// Get location from LocationProvider.Callback interface
|
||||||
|
double lat = location.getLatitude();
|
||||||
|
double lon = location.getLongitude();
|
||||||
|
Log.d(TAG, "setResult: started");
|
||||||
|
Log.d(TAG, "onNext: lat = " + lat);
|
||||||
|
Log.d(TAG, "onNext: long = " + lon);
|
||||||
|
|
||||||
|
// Save longitude and latitude values via PrefManager
|
||||||
|
prefManager.saveLatitude(String.valueOf(lat));
|
||||||
|
prefManager.saveLongitude(String.valueOf(lon));
|
||||||
|
|
||||||
|
// Show tab fragments
|
||||||
|
initUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void locationTurnedOff() {
|
||||||
|
// Show an alert dialog if location is turned off
|
||||||
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
|
TurnedOffLocationDialog alertDialog = TurnedOffLocationDialog.newInstance();
|
||||||
|
alertDialog.show(fm, "fragment_alert");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initUI() {
|
||||||
|
// Show tab fragments
|
||||||
|
TabsPagerAdapter tabsPagerAdapter = new TabsPagerAdapter(this, getSupportFragmentManager());
|
||||||
|
ViewPager viewPager = findViewById(R.id.view_pager);
|
||||||
|
viewPager.setAdapter(tabsPagerAdapter);
|
||||||
|
TabLayout tabs = findViewById(R.id.tabs);
|
||||||
|
tabs.setupWithViewPager(viewPager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showInformationDialog(String title, String message) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
|
||||||
|
builder.setTitle(title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setIcon(R.drawable.ic_info_outline_black_24dp)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setNegativeButton("OK", (dialog, id) -> dialog.cancel());
|
||||||
|
AlertDialog alert = builder.create();
|
||||||
|
alert.show();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.bernd32.weatherdemo.ui;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This dialog fragment is showed if user's location if turned off
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class TurnedOffLocationDialog extends DialogFragment {
|
||||||
|
|
||||||
|
private static final String TAG = "TurnedOffLocationDialog";
|
||||||
|
|
||||||
|
public TurnedOffLocationDialog() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TurnedOffLocationDialog newInstance() {
|
||||||
|
TurnedOffLocationDialog frag = new TurnedOffLocationDialog();
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreateDialog: started");
|
||||||
|
// Use the Builder class for convenient dialog construction
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
builder.setMessage(R.string.location_turned_off)
|
||||||
|
.setPositiveButton(R.string.turn_on_location, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.enter_a_city, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
startActivity(new Intent(getContext(), AddNewLocation.class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Create the AlertDialog object and return it
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
package com.bernd32.weatherdemo.ui.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
import com.bernd32.weatherdemo.models.UserLocation;
|
||||||
|
import com.bernd32.weatherdemo.ui.MainActivity;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RecyclerView adapter used in ui.AddNewLocation activity
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class LocationsRecyclerAdapter extends RecyclerView.Adapter<LocationsRecyclerAdapter.BaseViewHolder> {
|
||||||
|
|
||||||
|
private List<UserLocation> mItems;
|
||||||
|
private Context mContext;
|
||||||
|
private static final String TAG = "LocationsRecyclerAdapter";
|
||||||
|
|
||||||
|
public LocationsRecyclerAdapter(Context context, List<UserLocation> items) {
|
||||||
|
this.mItems = items;
|
||||||
|
this.mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItems(List<UserLocation> postItems) {
|
||||||
|
mItems.addAll(postItems);
|
||||||
|
mItems = postItems;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
mItems.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserLocation getItem(int position) {
|
||||||
|
return mItems.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public LocationsRecyclerAdapter.BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.location_item, parent, false);
|
||||||
|
return new BaseViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
|
||||||
|
UserLocation currentItem = mItems.get(position);
|
||||||
|
|
||||||
|
// Since we have location names in getCity(), we should capitalize the first letter
|
||||||
|
String cityName = currentItem.getCity();
|
||||||
|
String capitalizedCityName = cityName.substring(0, 1).toUpperCase() + cityName.substring(1);
|
||||||
|
|
||||||
|
holder.itemValue.setText(capitalizedCityName);
|
||||||
|
|
||||||
|
holder.parentLayout.setOnClickListener(view -> {
|
||||||
|
Intent intent = new Intent(mContext, MainActivity.class);
|
||||||
|
intent.putExtra("city", holder.itemValue.getText().toString());
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show a tooltip on a long click
|
||||||
|
holder.parentLayout.setOnLongClickListener(view -> {
|
||||||
|
Toast.makeText(mContext, mContext.getString(R.string.tooltip_msg), Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mItems == null ? 0 : mItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class BaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private TextView itemValue;
|
||||||
|
MaterialCardView parentLayout;
|
||||||
|
|
||||||
|
BaseViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
itemValue = itemView.findViewById(R.id.item_value);
|
||||||
|
parentLayout = itemView.findViewById(R.id.parent_layout);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package com.bernd32.weatherdemo.ui.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
import com.bernd32.weatherdemo.models.WeatherItem;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RecyclerView adapter used in our fragments (ui.CurrentWeatherFragment and ui.ForecastFragment)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.BaseViewHolder> {
|
||||||
|
|
||||||
|
private ArrayList<WeatherItem> mWeatherItems;
|
||||||
|
private Context mContext;
|
||||||
|
private static final String TAG = "RecyclerAdapter";
|
||||||
|
|
||||||
|
public RecyclerAdapter(Context context, ArrayList<WeatherItem> items) {
|
||||||
|
this.mWeatherItems = items;
|
||||||
|
this.mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private ImageView imageView;
|
||||||
|
private TextView titleText, valueText;
|
||||||
|
|
||||||
|
BaseViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
imageView = itemView.findViewById(R.id.image);
|
||||||
|
titleText = itemView.findViewById(R.id.card_title);
|
||||||
|
valueText = itemView.findViewById(R.id.card_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RecyclerAdapter.BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.forecast_item, parent, false);
|
||||||
|
return new BaseViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
|
||||||
|
WeatherItem currentItem = mWeatherItems.get(position);
|
||||||
|
holder.imageView.setImageResource(currentItem.getImgResource());
|
||||||
|
if (currentItem.getImgResource() != 0) {
|
||||||
|
holder.imageView.setImageResource(currentItem.getImgResource());
|
||||||
|
} else if (currentItem.getImgUrl() != null) {
|
||||||
|
Glide.with(mContext).load(currentItem.getImgUrl()).into(holder.imageView);
|
||||||
|
}
|
||||||
|
holder.titleText.setText(currentItem.getTitle());
|
||||||
|
holder.valueText.setText(currentItem.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mWeatherItems.size();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.bernd32.weatherdemo.ui.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
|
|
||||||
|
import com.bernd32.weatherdemo.R;
|
||||||
|
import com.bernd32.weatherdemo.ui.CurrentWeatherFragment;
|
||||||
|
import com.bernd32.weatherdemo.ui.ForecastFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [FragmentPagerAdapter] that returns a fragment corresponding to
|
||||||
|
* one of the sections/tabs/pages.
|
||||||
|
*/
|
||||||
|
public class TabsPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
|
@StringRes
|
||||||
|
private static final int[] TAB_TITLES =
|
||||||
|
new int[]{R.string.tab_text_1, R.string.tab_text_2};
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
public TabsPagerAdapter(Context context, FragmentManager fm) {
|
||||||
|
super(fm);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
// getItem is called to instantiate the fragment for the given page.
|
||||||
|
if (position == 0) {
|
||||||
|
return CurrentWeatherFragment.newInstance();
|
||||||
|
} else {
|
||||||
|
return ForecastFragment.newInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public CharSequence getPageTitle(int position) {
|
||||||
|
return mContext.getResources().getString(TAB_TITLES[position]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
package com.bernd32.weatherdemo.utils;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
|
||||||
|
import com.google.android.gms.location.FusedLocationProviderClient;
|
||||||
|
import com.google.android.gms.location.LocationCallback;
|
||||||
|
import com.google.android.gms.location.LocationRequest;
|
||||||
|
import com.google.android.gms.location.LocationResult;
|
||||||
|
import com.google.android.gms.location.LocationServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides user's location coordinates, it is also checking permissions
|
||||||
|
* and requesting location data. Part of the code were taken from here:
|
||||||
|
* https://www.androdocs.com/java/getting-current-location-latitude-longitude-in-android-using-java.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class LocationProvider {
|
||||||
|
|
||||||
|
public static final int PERMISSION_ID = 44;
|
||||||
|
private FusedLocationProviderClient mFusedLocationClient;
|
||||||
|
private Context mContext;
|
||||||
|
private Callback mCallback;
|
||||||
|
private static final String TAG = "LocationProvider";
|
||||||
|
|
||||||
|
public LocationProvider(Context context, Callback callback) {
|
||||||
|
this.mCallback = callback;
|
||||||
|
this.mContext = context;
|
||||||
|
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getLastLocation(){
|
||||||
|
Log.d(TAG, "getLastLocation: started");
|
||||||
|
if (checkPermissions()) {
|
||||||
|
if (isLocationEnabled()) {
|
||||||
|
mFusedLocationClient.getLastLocation().addOnCompleteListener(
|
||||||
|
task -> {
|
||||||
|
Location location = task.getResult();
|
||||||
|
if (location == null) {
|
||||||
|
LocationProvider.this.requestNewLocationData();
|
||||||
|
} else {
|
||||||
|
mCallback.setResult(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
mCallback.locationTurnedOff();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestPermissions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
private void requestNewLocationData(){
|
||||||
|
Log.d(TAG, "requestNewLocationData: ");
|
||||||
|
LocationRequest mLocationRequest = new LocationRequest();
|
||||||
|
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
|
||||||
|
mLocationRequest.setInterval(0);
|
||||||
|
mLocationRequest.setFastestInterval(0);
|
||||||
|
mLocationRequest.setNumUpdates(1);
|
||||||
|
|
||||||
|
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(mContext);
|
||||||
|
mFusedLocationClient.requestLocationUpdates(
|
||||||
|
mLocationRequest, mLocationCallback,
|
||||||
|
Looper.myLooper()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocationCallback mLocationCallback = new LocationCallback() {
|
||||||
|
@Override
|
||||||
|
public void onLocationResult(LocationResult locationResult) {
|
||||||
|
Log.d(TAG, "onLocationResult: started");
|
||||||
|
Location mLocation = locationResult.getLastLocation();
|
||||||
|
mCallback.setResult(mLocation);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private boolean checkPermissions() {
|
||||||
|
return ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
|
||||||
|
ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestPermissions() {
|
||||||
|
Log.d(TAG, "requestPermissions: ");
|
||||||
|
ActivityCompat.requestPermissions(
|
||||||
|
(Activity) mContext,
|
||||||
|
new String[]{
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
},
|
||||||
|
PERMISSION_ID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLocationEnabled() {
|
||||||
|
LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(
|
||||||
|
LocationManager.NETWORK_PROVIDER
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
void setResult(Location location);
|
||||||
|
void locationTurnedOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 bernd32
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.bernd32.weatherdemo.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PrefManager utility to save/load/clear data
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PreferencesManager {
|
||||||
|
|
||||||
|
/* a thread-safe singleton class to make the global access method
|
||||||
|
synchronized, so that only one thread can execute this method at a time */
|
||||||
|
|
||||||
|
private static final String APP_PREFS = "com.bernd32.jlyrics.prefs";
|
||||||
|
private static final String LAT = "com.example.app.lat";
|
||||||
|
private static final String LON = "com.example.app.lon";
|
||||||
|
private static final String CITY = "com.example.app.city";
|
||||||
|
|
||||||
|
|
||||||
|
private static PreferencesManager sInstance;
|
||||||
|
private final SharedPreferences mPref;
|
||||||
|
|
||||||
|
private PreferencesManager(Context context) {
|
||||||
|
mPref = context.getSharedPreferences(APP_PREFS, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void initializeInstance(Context context) {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new PreferencesManager(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized PreferencesManager getInstance() {
|
||||||
|
if (sInstance == null) {
|
||||||
|
throw new IllegalStateException(PreferencesManager.class.getSimpleName() +
|
||||||
|
" is not initialized, call initializeInstance(..) method first.");
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveCity(String value) {
|
||||||
|
mPref.edit()
|
||||||
|
.putString(CITY, value)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCity() {
|
||||||
|
return mPref.getString(CITY, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveLatitude(String value) {
|
||||||
|
mPref.edit()
|
||||||
|
.putString(LAT, value)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveLongitude(String value) {
|
||||||
|
mPref.edit()
|
||||||
|
.putString(LON, value)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getLatitude() {
|
||||||
|
return mPref.getString(LAT, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLongitude() {
|
||||||
|
return mPref.getString(LON, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(String key) {
|
||||||
|
mPref.edit()
|
||||||
|
.remove(key)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean clear() {
|
||||||
|
return mPref.edit()
|
||||||
|
.clear()
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
}
|
5
app/src/main/res/drawable-v24/ic_call.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="72dp" android:viewportHeight="1024"
|
||||||
|
android:viewportWidth="1024" android:width="72dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#5CA4F6" android:pathData="M659.2,717.95a673.73,673.73 0,0 1,75.33 57.02,458.75 458.75,0 0,1 41.34,41.09c17.15,20.03 25.86,38.02 25.47,55.62 -0.58,20.22 -12.61,40.77 -35.84,62.4 -35.52,34.88 -88.26,57.15 -148.29,57.15 -24,0 -48.7,-3.84 -73.41,-11.33a338.43,338.43 0,0 1,-78.78 -36.29c-105.34,-62.21 -186.37,-142.02 -263.49,-259.39 -56.77,-86.4 -95.87,-176 -117.7,-266.62 -12.74,-52.93 -18.56,-101.25 -17.92,-139.01 0.13,-62.46 21.76,-115.07 62.78,-157.7a237.57,237.57 0,0 1,72.83 -52.29c18.82,-8.7 35.01,-12.99 49.86,-12.99 6.78,0 13.25,0.9 19.2,2.75 17.66,5.31 31.49,18.18 40.96,38.66 17.47,37.76 34.05,91.9 45.31,148.29 8.7,43.01 -1.73,59.39 -50.11,83.46l-10.3,4.35 -10.18,4.22 -13.44,5.57 -32.7,13.63a29.18,29.18 0,0 0,-16.58 35.97A933.76,933.76 0,0 0,326.08 603.52a940.99,940.99 0,0 0,148.22 177.66,28.99 28.99,0 0,0 19.2,7.81l2.37,-0.13a29.38,29.38 0,0 0,21.12 -11.07c11.9,-15.36 24,-29.06 35.2,-39.68 24.64,-23.42 46.72,-35.2 67.65,-35.2 5.44,0 10.82,0.77 16,2.37 4.99,1.54 13.82,6.4 23.36,12.67zM666.56,512.51A183.62,183.62 0,0 0,522.24 320.38a25.6,25.6 0,1 0,-10.82 50.05,132.35 132.35,0 0,1 103.94,138.56 25.6,25.6 0,0 0,51.14 3.52zM800.51,525.12a318.21,318.21 0,0 0,-230.4 -331.46,25.6 25.6,0 0,0 -13.89,49.28 266.88,266.88 0,0 1,193.22 278.14,25.6 25.6,0 0,0 51.07,4.03z"/>
|
||||||
|
<path android:fillColor="#5CA4F6" android:pathData="M942.08,535.42A460.16,460.16 0,0 0,611.01 57.73a25.6,25.6 0,1 0,-14.21 49.28,408.96 408.96,0 0,1 294.14,424.45 25.6,25.6 0,0 0,51.14 3.97z"/>
|
||||||
|
</vector>
|
34
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="78.5885"
|
||||||
|
android:endY="90.9159"
|
||||||
|
android:startX="48.7653"
|
||||||
|
android:startY="61.0927"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_add_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||||
|
</vector>
|
4
app/src/main/res/drawable/ic_call_black_24dp.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<vector android:height="50dp" android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0" android:width="50dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_info_outline_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
|
||||||
|
</vector>
|
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#008577"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_location_on_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_refresh_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_settings_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_wi_barometer.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="30dp"
|
||||||
|
android:height="30dp"
|
||||||
|
android:viewportWidth="30"
|
||||||
|
android:viewportHeight="30">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M7.69,13.2c0,-0.99 0.19,-1.94 0.58,-2.85c0.39,-0.91 0.91,-1.68 1.57,-2.33s1.44,-1.17 2.34,-1.56c0.9,-0.39 1.85,-0.58 2.84,-0.58c0.99,0 1.94,0.19 2.85,0.58c0.9,0.39 1.68,0.91 2.33,1.56c0.65,0.65 1.17,1.43 1.56,2.33s0.58,1.85 0.58,2.85c0,1.62 -0.48,3.06 -1.44,4.34c-0.96,1.27 -2.2,2.14 -3.71,2.61v3.29h-4.24v-3.25c-1.54,-0.45 -2.81,-1.32 -3.79,-2.61S7.69,14.83 7.69,13.2zM9.3,13.2c0,1.55 0.56,2.88 1.69,3.99c1.11,1.12 2.45,1.68 4.02,1.68c1.03,0 1.99,-0.25 2.86,-0.76c0.88,-0.51 1.57,-1.2 2.09,-2.07c0.51,-0.87 0.77,-1.82 0.77,-2.85c0,-0.77 -0.15,-1.5 -0.45,-2.21s-0.71,-1.31 -1.22,-1.82c-0.51,-0.51 -1.12,-0.92 -1.83,-1.22c-0.71,-0.3 -1.44,-0.45 -2.21,-0.45c-0.77,0 -1.5,0.15 -2.21,0.45s-1.31,0.71 -1.82,1.22c-0.51,0.51 -0.92,1.12 -1.22,1.82C9.45,11.7 9.3,12.43 9.3,13.2zM9.88,13.56v-0.72h2.17v0.72H9.88zM10.97,10.02l0.52,-0.52l1.52,1.52l-0.52,0.53L10.97,10.02zM13.5,14.95c0,-0.42 0.15,-0.78 0.44,-1.09c0.29,-0.31 0.65,-0.47 1.06,-0.48l2.73,-4.49l0.66,0.35l-2.02,4.83c0.18,0.25 0.26,0.54 0.26,0.88c0,0.44 -0.15,0.81 -0.46,1.11c-0.31,0.3 -0.68,0.45 -1.12,0.45c-0.43,0 -0.8,-0.15 -1.1,-0.45C13.65,15.76 13.5,15.39 13.5,14.95zM14.81,10.28V8.12h0.69v2.17H14.81zM17.75,13.55v-0.74h2.17v0.74H17.75z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_wi_day_windy.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="30dp"
|
||||||
|
android:height="30dp"
|
||||||
|
android:viewportWidth="30"
|
||||||
|
android:viewportHeight="30">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M1.48,21.1c0,0.24 0.09,0.44 0.27,0.6c0.17,0.17 0.37,0.25 0.61,0.25h5.88c0.26,0 0.48,0.09 0.68,0.28c0.2,0.19 0.3,0.42 0.3,0.68s-0.1,0.5 -0.3,0.69c-0.2,0.19 -0.42,0.29 -0.68,0.29c-0.26,0 -0.48,-0.1 -0.68,-0.3c-0.18,-0.16 -0.38,-0.24 -0.61,-0.24c-0.24,0 -0.44,0.08 -0.6,0.24c-0.16,0.16 -0.24,0.36 -0.24,0.6c0,0.24 0.08,0.44 0.24,0.6c0.53,0.53 1.16,0.8 1.89,0.8c0.74,0 1.37,-0.26 1.88,-0.78s0.78,-1.15 0.78,-1.89s-0.26,-1.37 -0.78,-1.89c-0.52,-0.52 -1.15,-0.78 -1.88,-0.78H2.36c-0.24,0 -0.44,0.08 -0.62,0.25C1.57,20.67 1.48,20.87 1.48,21.1zM1.48,18.09c0,0.23 0.09,0.42 0.27,0.58c0.16,0.16 0.37,0.24 0.61,0.24h10.97c0.74,0 1.37,-0.26 1.89,-0.78c0.52,-0.52 0.78,-1.15 0.78,-1.89c0,-0.74 -0.26,-1.36 -0.78,-1.88c-0.52,-0.51 -1.15,-0.77 -1.89,-0.77c-0.76,0 -1.38,0.25 -1.88,0.76c-0.16,0.16 -0.23,0.37 -0.23,0.61c0,0.24 0.08,0.44 0.23,0.6c0.15,0.15 0.35,0.23 0.6,0.23c0.24,0 0.44,-0.08 0.62,-0.23c0.19,-0.19 0.41,-0.28 0.68,-0.28s0.49,0.09 0.68,0.28s0.29,0.42 0.29,0.68c0,0.27 -0.1,0.5 -0.29,0.69c-0.19,0.19 -0.42,0.29 -0.68,0.29H2.36c-0.24,0 -0.44,0.09 -0.62,0.26C1.57,17.66 1.48,17.86 1.48,18.09zM7.27,11.55c0,-0.24 0.09,-0.44 0.26,-0.62c0.18,-0.16 0.38,-0.24 0.6,-0.24h2.03c0.23,0 0.42,0.08 0.58,0.25c0.16,0.17 0.23,0.37 0.23,0.61c0,0.24 -0.08,0.44 -0.23,0.6c-0.16,0.17 -0.35,0.25 -0.58,0.25H8.13c-0.24,0 -0.44,-0.08 -0.61,-0.25C7.35,11.98 7.27,11.78 7.27,11.55zM10.12,4.63c0,-0.23 0.08,-0.43 0.23,-0.61c0.19,-0.16 0.41,-0.24 0.64,-0.24c0.22,0 0.42,0.08 0.59,0.24l1.43,1.47c0.16,0.15 0.24,0.35 0.24,0.59c0,0.24 -0.08,0.44 -0.24,0.6s-0.36,0.24 -0.59,0.24c-0.24,0 -0.44,-0.08 -0.59,-0.24l-1.47,-1.43C10.2,5.08 10.12,4.88 10.12,4.63zM12.68,11.43v-0.07c0.02,-0.91 0.27,-1.75 0.74,-2.53c0.48,-0.77 1.11,-1.38 1.9,-1.83c0.79,-0.45 1.65,-0.67 2.57,-0.67c0.7,0 1.37,0.14 2.02,0.42c0.64,0.28 1.2,0.65 1.66,1.12c0.47,0.47 0.84,1.02 1.11,1.66s0.41,1.32 0.41,2.02c0,0.94 -0.23,1.8 -0.69,2.6s-1.09,1.43 -1.88,1.89c-0.79,0.47 -1.66,0.7 -2.6,0.71h-0.21c-0.07,0 -0.13,-0.02 -0.17,-0.07c-0.05,-0.05 -0.07,-0.11 -0.07,-0.18v-1.22c0,-0.13 0.07,-0.2 0.22,-0.2h0.24c0.96,-0.01 1.79,-0.35 2.47,-1.05c0.68,-0.69 1.03,-1.52 1.03,-2.49c0,-0.96 -0.35,-1.78 -1.04,-2.47c-0.69,-0.68 -1.52,-1.02 -2.5,-1.02c-0.94,0 -1.76,0.32 -2.44,0.98c-0.68,0.65 -1.04,1.44 -1.08,2.37c0,0.06 -0.03,0.11 -0.08,0.17s-0.14,0.09 -0.26,0.09H12.9C12.75,11.67 12.68,11.59 12.68,11.43zM17.03,21.31v-1.99c0,-0.24 0.08,-0.44 0.25,-0.6s0.37,-0.24 0.6,-0.24c0.25,0 0.45,0.08 0.61,0.24c0.16,0.16 0.24,0.36 0.24,0.6v1.99c0,0.24 -0.08,0.45 -0.25,0.62c-0.17,0.17 -0.37,0.25 -0.6,0.25c-0.24,0 -0.44,-0.08 -0.6,-0.25C17.12,21.76 17.03,21.55 17.03,21.31zM17.03,3.83V1.78c0,-0.23 0.08,-0.43 0.25,-0.6s0.37,-0.25 0.6,-0.25c0.24,0 0.44,0.08 0.6,0.25s0.25,0.37 0.25,0.6v2.04c0,0.23 -0.08,0.42 -0.25,0.58c-0.17,0.15 -0.37,0.23 -0.6,0.23c-0.24,0 -0.44,-0.08 -0.6,-0.23C17.12,4.25 17.03,4.06 17.03,3.83zM22.56,17.01c0,-0.23 0.08,-0.42 0.23,-0.56c0.15,-0.16 0.34,-0.23 0.57,-0.23c0.24,0 0.44,0.08 0.6,0.23l1.45,1.42c0.16,0.17 0.24,0.38 0.24,0.61c0,0.23 -0.08,0.43 -0.24,0.59c-0.4,0.31 -0.8,0.31 -1.2,0l-1.42,-1.43C22.63,17.48 22.56,17.26 22.56,17.01zM22.56,6.08c0,-0.25 0.08,-0.45 0.23,-0.59l1.42,-1.47c0.18,-0.16 0.37,-0.24 0.59,-0.24c0.23,0 0.43,0.08 0.6,0.25c0.17,0.17 0.25,0.37 0.25,0.6c0,0.25 -0.08,0.46 -0.24,0.62l-1.45,1.43c-0.18,0.16 -0.38,0.24 -0.6,0.24c-0.23,0 -0.41,-0.08 -0.57,-0.24C22.63,6.52 22.56,6.32 22.56,6.08zM24.82,11.55c0,-0.24 0.08,-0.44 0.24,-0.62c0.16,-0.16 0.35,-0.24 0.57,-0.24h2.02c0.23,0 0.43,0.09 0.61,0.26s0.26,0.37 0.26,0.6c0,0.23 -0.09,0.43 -0.26,0.6c-0.18,0.17 -0.38,0.25 -0.61,0.25h-2.02c-0.23,0 -0.42,-0.08 -0.58,-0.25C24.89,11.99 24.82,11.79 24.82,11.55z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_wi_humidity.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="30dp"
|
||||||
|
android:height="30dp"
|
||||||
|
android:viewportWidth="30"
|
||||||
|
android:viewportHeight="30">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M7.56,17.19c0,-0.88 0.24,-1.89 0.72,-3.03s1.1,-2.25 1.86,-3.31c1.56,-2.06 2.92,-3.62 4.06,-4.67l0.75,-0.72c0.25,0.26 0.53,0.5 0.83,0.72c0.41,0.42 1.04,1.11 1.88,2.09s1.57,1.85 2.17,2.65c0.71,1.01 1.32,2.1 1.81,3.25s0.74,2.16 0.74,3.03c0,1 -0.19,1.95 -0.58,2.86c-0.39,0.91 -0.91,1.7 -1.57,2.36c-0.66,0.66 -1.45,1.19 -2.37,1.58c-0.92,0.39 -1.89,0.59 -2.91,0.59c-1,0 -1.95,-0.19 -2.86,-0.57c-0.91,-0.38 -1.7,-0.89 -2.36,-1.55c-0.66,-0.65 -1.19,-1.44 -1.58,-2.35S7.56,18.23 7.56,17.19zM9.82,14.26c0,0.83 0.17,1.49 0.52,1.99c0.35,0.49 0.88,0.74 1.59,0.74c0.72,0 1.25,-0.25 1.61,-0.74c0.35,-0.49 0.53,-1.15 0.54,-1.99c-0.01,-0.84 -0.19,-1.5 -0.54,-2c-0.35,-0.49 -0.89,-0.74 -1.61,-0.74c-0.71,0 -1.24,0.25 -1.59,0.74C9.99,12.76 9.82,13.42 9.82,14.26zM11.39,14.26c0,-0.15 0,-0.27 0,-0.35s0.01,-0.19 0.02,-0.33c0.01,-0.14 0.02,-0.25 0.05,-0.32s0.05,-0.16 0.09,-0.24c0.04,-0.08 0.09,-0.15 0.15,-0.18c0.07,-0.04 0.14,-0.06 0.23,-0.06c0.14,0 0.25,0.04 0.33,0.12s0.14,0.21 0.17,0.38c0.03,0.18 0.05,0.32 0.06,0.45s0.01,0.3 0.01,0.52c0,0.23 0,0.4 -0.01,0.52c-0.01,0.12 -0.03,0.27 -0.06,0.45c-0.03,0.17 -0.09,0.3 -0.17,0.38s-0.19,0.12 -0.33,0.12c-0.09,0 -0.16,-0.02 -0.23,-0.06c-0.07,-0.04 -0.12,-0.1 -0.15,-0.18c-0.04,-0.08 -0.07,-0.17 -0.09,-0.24c-0.02,-0.08 -0.04,-0.19 -0.05,-0.32c-0.01,-0.14 -0.02,-0.25 -0.02,-0.32S11.39,14.41 11.39,14.26zM11.98,22.01h1.32l4.99,-10.74h-1.35L11.98,22.01zM16.28,19.02c0.01,0.84 0.2,1.5 0.55,2c0.35,0.49 0.89,0.74 1.6,0.74c0.72,0 1.25,-0.25 1.6,-0.74c0.35,-0.49 0.52,-1.16 0.53,-2c-0.01,-0.84 -0.18,-1.5 -0.53,-1.99c-0.35,-0.49 -0.88,-0.74 -1.6,-0.74c-0.71,0 -1.25,0.25 -1.6,0.74C16.47,17.52 16.29,18.18 16.28,19.02zM17.85,19.02c0,-0.23 0,-0.4 0.01,-0.52c0.01,-0.12 0.03,-0.27 0.06,-0.45s0.09,-0.3 0.17,-0.38s0.19,-0.12 0.33,-0.12c0.09,0 0.17,0.02 0.24,0.06c0.07,0.04 0.12,0.1 0.16,0.19c0.04,0.09 0.07,0.17 0.1,0.24s0.04,0.18 0.05,0.32l0.01,0.32l0,0.34c0,0.16 0,0.28 0,0.35l-0.01,0.32l-0.05,0.32l-0.1,0.24l-0.16,0.19l-0.24,0.06c-0.14,0 -0.25,-0.04 -0.33,-0.12s-0.14,-0.21 -0.17,-0.38c-0.03,-0.18 -0.05,-0.33 -0.06,-0.45S17.85,19.25 17.85,19.02z"/>
|
||||||
|
</vector>
|
8
app/src/main/res/drawable/location_cardview_item_bg.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:type="linear"
|
||||||
|
android:angle="0"
|
||||||
|
android:startColor="#00BCD4"
|
||||||
|
android:endColor="#1976D2" />
|
||||||
|
</shape>
|
7
app/src/main/res/font/raleway_semibold.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
app:fontProviderAuthority="com.google.android.gms.fonts"
|
||||||
|
app:fontProviderPackage="com.google.android.gms"
|
||||||
|
app:fontProviderQuery="name=Raleway&weight=600"
|
||||||
|
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
|
||||||
|
</font-family>
|
68
app/src/main/res/layout/activity_add_new_location.xml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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"
|
||||||
|
android:padding="12dp"
|
||||||
|
tools:context=".ui.AddNewLocation">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:text="@string/add_a_new_city" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/city_edit_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="10"
|
||||||
|
android:hint="@string/city_name"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_add"
|
||||||
|
android:onClick="onAddButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/add" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_message"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/empty_message"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="false"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view_locations"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/design_default_color_background"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
34
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.MainActivity"
|
||||||
|
>
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/ThemeOverlay.MaterialComponents.ActionBar"
|
||||||
|
>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tabs"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:fillViewport="true"
|
||||||
|
app:tabMode="fixed"
|
||||||
|
style="@style/Widget.MaterialComponents.TabLayout.Colored"
|
||||||
|
/>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.viewpager.widget.ViewPager
|
||||||
|
android:id="@+id/view_pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
/>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
64
app/src/main/res/layout/forecast_item.xml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:cardCornerRadius="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:src="@drawable/location_cardview_item_bg"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="fitXY"/>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:contentDescription="@string/icon"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@android:color/darker_gray" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/card_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="Title"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/image"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/card_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Value"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/image"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/card_title"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
106
app/src/main/res/layout/fragment_current_weather.xml
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView 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="fill_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="false"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/current_weather_fragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/temperature_tv"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:freezesText="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Display3"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/city_tv"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/weatherText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:freezesText="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/temperature_tv"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_message"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:text="City list is empty. Add a city! "
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/weatherText" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/city_tv"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:freezesText="true"
|
||||||
|
android:drawableStart="@drawable/ic_location_on_black_24dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/weatherIcon"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:contentDescription="@string/weather_icon"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/weatherText" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@color/design_default_color_background"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/weatherIcon" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:layout_editor_absoluteX="182dp"
|
||||||
|
tools:layout_editor_absoluteY="326dp" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
27
app/src/main/res/layout/fragment_forecast.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView 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="fill_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="false"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp"
|
||||||
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/design_default_color_background"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
36
app/src/main/res/layout/location_item.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:cardMaxElevation="0dp"
|
||||||
|
android:id="@+id/parent_layout"
|
||||||
|
app:cardCornerRadius="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:src="@drawable/location_cardview_item_bg"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="fitXY"/>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#000000"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
9
app/src/main/res/layout/settings_activity.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</LinearLayout>
|
7
app/src/main/res/menu/add_new_location_menu.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item android:id="@+id/delete_all"
|
||||||
|
android:title="@string/delete_all"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
17
app/src/main/res/menu/main_menu.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item android:id="@+id/open_saved_locations"
|
||||||
|
android:title="@string/add_new_location"
|
||||||
|
android:icon="@drawable/ic_add_black_24dp"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
<item android:id="@+id/update"
|
||||||
|
android:title="@string/update"
|
||||||
|
android:icon="@drawable/ic_refresh_black_24dp"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
<item android:id="@+id/about_dialog"
|
||||||
|
android:title="@string/about"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:icon="@drawable/ic_info_outline_black_24dp" />
|
||||||
|
|
||||||
|
</menu>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 11 KiB |
33
app/src/main/res/values-ru/strings.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Weather Demo App</string>
|
||||||
|
<string name="about">О программе</string>
|
||||||
|
<string name="about_message">Weather App Demo 1.0.0\n\nСделано bernd32 с целью практики использования Retrofit и RxJava.\n\nМой Github: https://github.com/bernd32\nEmail: fallentides@outlook.com</string>
|
||||||
|
<string name="add">Добавить</string>
|
||||||
|
<string name="add_a_new_city">Добавить новый город:</string>
|
||||||
|
<string name="add_new_location">"Добавить новое местоположение… "</string>
|
||||||
|
<string name="all_items_deleted_msg">Очищено</string>
|
||||||
|
<string name="city_name">Название города (например: Екатеринбург)</string>
|
||||||
|
<string name="current_location">Текущее местоположение</string>
|
||||||
|
<string name="delete_all">Очистить</string>
|
||||||
|
<string name="delete_item_msg">Местоположение удалено</string>
|
||||||
|
<string name="empty_message">Список пуст…</string>
|
||||||
|
<string name="enter_a_city">Введите название города</string>
|
||||||
|
<string name="error">Ошибка</string>
|
||||||
|
<string name="humidity">Влажность</string>
|
||||||
|
<string name="icon">Иконка</string>
|
||||||
|
<string name="location">Местоположение</string>
|
||||||
|
<string name="location_turned_off">Включить локацию</string>
|
||||||
|
<string name="pressure">Давление</string>
|
||||||
|
<string name="save">Сохранить</string>
|
||||||
|
<string name="tab_text_1">Текущая погода</string>
|
||||||
|
<string name="tab_text_2">Прогноз</string>
|
||||||
|
<string name="tooltip_msg">Свайпните элемент для удаления</string>
|
||||||
|
<string name="turn_on_location">Включить локацию</string>
|
||||||
|
<string name="update">Обновить</string>
|
||||||
|
<string name="updated_msg">Обновлено</string>
|
||||||
|
<string name="weather_icon">Иконка</string>
|
||||||
|
<string name="wind">Скорость ветра</string>
|
||||||
|
<string name="meters_per_second">м/с</string>
|
||||||
|
<string name="pressure_unit">гПа</string>
|
||||||
|
</resources>
|
12
app/src/main/res/values/arrays.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Reply Preference -->
|
||||||
|
<string-array name="reply_entries">
|
||||||
|
<item>Reply</item>
|
||||||
|
<item>Reply to all</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="reply_values">
|
||||||
|
<item>reply</item>
|
||||||
|
<item>reply_all</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
7
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#2196F3</color>
|
||||||
|
<color name="colorPrimaryDark">#1976D2</color>
|
||||||
|
<color name="colorAccent">#FF5252</color>
|
||||||
|
<color name="gray">#e3e3e3</color>
|
||||||
|
</resources>
|
8
app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
<dimen name="appbar_padding">16dp</dimen>
|
||||||
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
|
<dimen name="appbar_padding_top">8dp</dimen>
|
||||||
|
</resources>
|
17
app/src/main/res/values/font_certs.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<array name="com_google_android_gms_fonts_certs">
|
||||||
|
<item>@array/com_google_android_gms_fonts_certs_dev</item>
|
||||||
|
<item>@array/com_google_android_gms_fonts_certs_prod</item>
|
||||||
|
</array>
|
||||||
|
<string-array name="com_google_android_gms_fonts_certs_dev">
|
||||||
|
<item>
|
||||||
|
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
|
||||||
|
</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="com_google_android_gms_fonts_certs_prod">
|
||||||
|
<item>
|
||||||
|
MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
|
||||||
|
</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
|
</resources>
|
6
app/src/main/res/values/preloaded_fonts.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<array name="preloaded_fonts" translatable="false">
|
||||||
|
<item>@font/raleway_semibold</item>
|
||||||
|
</array>
|
||||||
|
</resources>
|
43
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Weather Demo App</string>
|
||||||
|
|
||||||
|
<string name="tab_text_1">Current weather</string>
|
||||||
|
<string name="tab_text_2">Forecast</string>
|
||||||
|
|
||||||
|
<!-- TODO: Remove or change this placeholder text -->
|
||||||
|
<string name="weather_icon">Weather icon</string>
|
||||||
|
<string name="icon">Icon</string>
|
||||||
|
<string name="pressure">Pressure</string>
|
||||||
|
<string name="wind">Wind</string>
|
||||||
|
<string name="humidity">Humidity</string>
|
||||||
|
<string name="location_turned_off">Turn on location</string>
|
||||||
|
<string name="current_location">Current location</string>
|
||||||
|
<string name="enter_a_city">Enter a city</string>
|
||||||
|
<string name="save">Save</string>
|
||||||
|
|
||||||
|
<!-- Preference Titles -->
|
||||||
|
|
||||||
|
<!-- Messages Preferences -->
|
||||||
|
|
||||||
|
<!-- Sync Preferences -->
|
||||||
|
<string name="location">Location</string>
|
||||||
|
<string name="update">Update…</string>
|
||||||
|
<string name="turn_on_location">Turn on location</string>
|
||||||
|
<string name="add_new_location">Add new location… </string>
|
||||||
|
<string name="add_a_new_city">Add a new city by name:</string>
|
||||||
|
<string name="city_name">City name (e.g.: London, UK)</string>
|
||||||
|
<string name="add">Add</string>
|
||||||
|
<string name="delete_item_msg">Location deleted</string>
|
||||||
|
<string name="tooltip_msg">Swipe it to delete</string>
|
||||||
|
<string name="empty_message">City list is empty…</string>
|
||||||
|
<string name="updated_msg">Updated</string>
|
||||||
|
<string name="about">About</string>
|
||||||
|
<string name="about_message">Weather Demo 1.0.0\n\nThis is a demo app created by
|
||||||
|
bernd32 for educational purposes.\n\nMy Github: https://github.com/bernd32\nEmail: fallentides@outlook.com
|
||||||
|
</string>
|
||||||
|
<string name="error">Error</string>
|
||||||
|
<string name="delete_all">Clear</string>
|
||||||
|
<string name="all_items_deleted_msg">Cleared</string>
|
||||||
|
<string name="meters_per_second">m/s</string>
|
||||||
|
<string name="pressure_unit">hPa</string>
|
||||||
|
</resources>
|
20
app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="Theme.MaterialComponents.Light">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.NoActionBar">
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||||
|
|
||||||
|
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
|
||||||
|
|
||||||
|
</resources>
|
6
app/src/main/res/xml/network_security_config.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">openweathermap.org</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.bernd32.weatherdemo;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.bernd32.weatherdemo.utils;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class LocationProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getLastLocation() {
|
||||||
|
}
|
||||||
|
}
|