Initial commit
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
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
|
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
|||||||
|
JLyrics
|
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>
|
BIN
JLyrics.zip
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
2
README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# jlyrics-android
|
||||||
|
An unoffical android client of j-lyrics.net
|
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
85
app/build.gradle
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
android {
|
||||||
|
// Kuromoji
|
||||||
|
packagingOptions {
|
||||||
|
exclude 'META-INF/CONTRIBUTORS.md'
|
||||||
|
exclude 'META-INF/LICENSE.md'
|
||||||
|
exclude 'META-INF/MANIFEST.MF'
|
||||||
|
exclude 'META-INF/NOTICE.md'
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileSdkVersion 29
|
||||||
|
buildToolsVersion "29.0.2"
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.bernd32.jlyrics"
|
||||||
|
minSdkVersion 23
|
||||||
|
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'
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
|
//Jsoup
|
||||||
|
implementation 'org.jsoup:jsoup:1.12.1'
|
||||||
|
// RecyclerView
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
|
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'
|
||||||
|
// Material
|
||||||
|
implementation 'com.google.android.material:material:1.2.0-alpha02'
|
||||||
|
|
||||||
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
||||||
|
|
||||||
|
implementation 'androidx.paging:paging-runtime:2.1.0'
|
||||||
|
// Room components
|
||||||
|
implementation "androidx.room:room-runtime:$rootProject.roomVersion"
|
||||||
|
annotationProcessor "androidx.room:room-compiler:$rootProject.roomVersion"
|
||||||
|
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
|
||||||
|
// Lifecycle components
|
||||||
|
implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.archLifecycleVersion"
|
||||||
|
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$rootProject.archLifecycleVersion"
|
||||||
|
// Junit
|
||||||
|
implementation 'androidx.test.ext:junit:1.1.2-alpha02'
|
||||||
|
// debugImplementation because LeakCanary should only run in debug builds.
|
||||||
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0'
|
||||||
|
// Romaji-henkan
|
||||||
|
implementation 'io.github.bernd32:romaji-henkan:0.0.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
|
62
app/roadmap/todo.txt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
1. Implement detailed search
|
||||||
|
✓1.1 Prepare UI
|
||||||
|
✓1.1.1 In MainActivity add spinner (song name, artist, words from lyrics)
|
||||||
|
✓1.1.2 Add "Detailed search button"
|
||||||
|
✓1.1.3 Show hidden UI elements: song name, artist and lyrics EditText and spinner (full, partial )
|
||||||
|
✓1.2 If user entered only artist, show artist search result
|
||||||
|
✓1.2.1 Modify SearchActivity.documentHandle()
|
||||||
|
✓1.3 Implement artist page
|
||||||
|
|
||||||
|
2. UI changes
|
||||||
|
✓2.1 Add Magnifying-glass icon on the search button
|
||||||
|
✓2.2 Add FAB button which scrolls the page all the way up, and which appears after user scrolled 2nd page
|
||||||
|
✓2.3 Change app's color theme
|
||||||
|
2.4 Add an additional search button (at the bottom of detailed search form)
|
||||||
|
|
||||||
|
✓3. Handle a no results situation
|
||||||
|
|
||||||
|
4. Add options menu
|
||||||
|
✓5.1 Enable dark theme
|
||||||
|
✓5.2 Quit app
|
||||||
|
✓5.3 Contact developer
|
||||||
|
✓5.4 Add/Remove to favorites
|
||||||
|
✓5.5 Copy lyrics
|
||||||
|
✓5.6 Change font size (dialog)
|
||||||
|
✓5.7 Open favorites
|
||||||
|
✓5.7.1 Create favorites activity
|
||||||
|
|
||||||
|
5. Fix lyrics page
|
||||||
|
✓4.1 Add toolbar
|
||||||
|
✓4.2 Make page scrollable
|
||||||
|
✓4.3 Scale font size of textview with pinch zoom (https://stackoverflow.com/questions/40203241/android-custom-shape-for-zoom-buttons)
|
||||||
|
✓4.4 Add option in toolbar to translate lyrics to romaji
|
||||||
|
✓4.5 Show ProgressBar when loading lyrics
|
||||||
|
✓4.6 Hide toolbar when scrolled
|
||||||
|
✓4.7 Finish making share button
|
||||||
|
|
||||||
|
✓6. Create "Add lyrics to favorites" feature (https://codelabs.developers.google.com/codelabs/android-room-with-a-view/)
|
||||||
|
✓6.1 Delete lyric by swiping
|
||||||
|
✓6.2 Delete lyric by long tap
|
||||||
|
✓6.3 In LyricsView, detect that if current lyrics is in the database
|
||||||
|
|
||||||
|
7. Catch all possible exceptions
|
||||||
|
|
||||||
|
8. Miscellaneous
|
||||||
|
✓8.1 Fix the progress bar bug: from artist-list to song
|
||||||
|
✓8.2 Add auto-increment ID field into the sql table
|
||||||
|
✓8.3 Add app icon
|
||||||
|
✓8.4 Change default picture in CardView
|
||||||
|
✓8.5 Optimize the code
|
||||||
|
✓8.5.1 Get rid of ButterKnife dependence
|
||||||
|
✓8.6 Create random user-agent generator
|
||||||
|
✓8.7 Adapt app's color theme in WebView
|
||||||
|
|
||||||
|
9. Convert lyrics to romaji
|
||||||
|
✓9.1 Install kuromoji dependence
|
||||||
|
✓9.2 Adapt jakaroma library
|
||||||
|
✓9.3 Execute romanize() in AsyncTask
|
||||||
|
✓9.4 Switch to original
|
||||||
|
✓9.5 Hide layout and show progressBar and cancel fab
|
||||||
|
|
||||||
|
✓ - completed
|
||||||
|
|
49
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.bernd32.jlyrics">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="false"
|
||||||
|
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.FuriganaActivity" />
|
||||||
|
<activity android:name=".ui.ArtistSongsActivity" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.FavoritesActivity"
|
||||||
|
android:parentActivityName=".ui.LauncherActivity" />
|
||||||
|
<activity android:name=".ui.ShowLyricsActivity" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.SearchActivity"
|
||||||
|
android:parentActivityName=".ui.LauncherActivity" />
|
||||||
|
<activity android:name=".ui.LauncherActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
BIN
app/src/main/ic_launcher-web.png
Normal file
After Width: | Height: | Size: 15 KiB |
43
app/src/main/java/com/bernd32/jlyrics/BaseViewHolder.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
public abstract class BaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private int mCurrentPosition;
|
||||||
|
|
||||||
|
protected BaseViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void clear();
|
||||||
|
|
||||||
|
public void onBind(int position) {
|
||||||
|
mCurrentPosition = position;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentPosition() {
|
||||||
|
return mCurrentPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
75
app/src/main/java/com/bernd32/jlyrics/Lyric.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics;
|
||||||
|
|
||||||
|
public class Lyric {
|
||||||
|
|
||||||
|
private String cardTitle;
|
||||||
|
private String cardDescription;
|
||||||
|
private String imgUrl;
|
||||||
|
private String songUrl;
|
||||||
|
private String artistUrl;
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
public String getArtistUrl() {
|
||||||
|
return artistUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArtistUrl(String artistUrl) {
|
||||||
|
this.artistUrl = artistUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCardTitle() {
|
||||||
|
return cardTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCardTitle(String cardTitle) {
|
||||||
|
this.cardTitle = cardTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCardDescription() {
|
||||||
|
return cardDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCardDescription(String cardDescription) {
|
||||||
|
this.cardDescription = cardDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImgUrl() {
|
||||||
|
return imgUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImgUrl(String imgUrl) {
|
||||||
|
this.imgUrl = imgUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSongUrl() {
|
||||||
|
return songUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSongUrl(String songUrl) {
|
||||||
|
this.songUrl = songUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
public abstract class PaginationListener extends RecyclerView.OnScrollListener {
|
||||||
|
|
||||||
|
public static final int PAGE_START = 1;
|
||||||
|
@NonNull
|
||||||
|
private LinearLayoutManager layoutManager;
|
||||||
|
private static final int PAGE_SIZE = 20;
|
||||||
|
|
||||||
|
public PaginationListener(@NonNull LinearLayoutManager layoutManager) {
|
||||||
|
this.layoutManager = layoutManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
|
||||||
|
int visibleItemCount = layoutManager.getChildCount();
|
||||||
|
int totalItemCount = layoutManager.getItemCount();
|
||||||
|
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
|
||||||
|
|
||||||
|
if (!isLoading() && !isLastPage()) {
|
||||||
|
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
|
||||||
|
&& firstVisibleItemPosition >= 0
|
||||||
|
&& totalItemCount >= PAGE_SIZE) {
|
||||||
|
loadMoreItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dy > 0 && firstVisibleItemPosition > 5) {
|
||||||
|
showFAB();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dy < 0 && firstVisibleItemPosition < 5)
|
||||||
|
hideFAB();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void showFAB();
|
||||||
|
|
||||||
|
protected abstract void hideFAB();
|
||||||
|
|
||||||
|
protected abstract void loadMoreItems();
|
||||||
|
|
||||||
|
protected abstract boolean isLastPage();
|
||||||
|
|
||||||
|
protected abstract boolean isLoading();
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.adapters;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
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.jlyrics.BaseViewHolder;
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
import com.bernd32.jlyrics.database.FavLyrics;
|
||||||
|
import com.bernd32.jlyrics.ui.SearchActivity;
|
||||||
|
import com.bernd32.jlyrics.ui.ShowLyricsActivity;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FavLyricsAdapter extends RecyclerView.Adapter<BaseViewHolder> {
|
||||||
|
private static final String TAG = "PostRecyclerAdapter";
|
||||||
|
private List<FavLyrics> mPostItems;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
public FavLyricsAdapter(Context context, List<FavLyrics> postItems) {
|
||||||
|
Log.d(TAG, "PostRecyclerAdapter: constructor activated");
|
||||||
|
this.mPostItems = postItems;
|
||||||
|
this.mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
return new ViewHolder(
|
||||||
|
LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem, parent, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
|
||||||
|
holder.onBind(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mPostItems == null ? 0 : mPostItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItems(List<FavLyrics> postItems) {
|
||||||
|
mPostItems.addAll(postItems);
|
||||||
|
mPostItems = postItems;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
mPostItems.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
Glide.get(mContext).clearMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FavLyrics getItem(int position) {
|
||||||
|
return mPostItems.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends BaseViewHolder {
|
||||||
|
TextView cardTitle;
|
||||||
|
TextView cardDescription;
|
||||||
|
ImageView image;
|
||||||
|
MaterialCardView parentLayout;
|
||||||
|
|
||||||
|
ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
cardTitle = itemView.findViewById(R.id.card_title);
|
||||||
|
cardDescription = itemView.findViewById(R.id.card_description);
|
||||||
|
image = itemView.findViewById(R.id.image);
|
||||||
|
parentLayout = itemView.findViewById(R.id.parent_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void clear() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBind(int position) {
|
||||||
|
super.onBind(position);
|
||||||
|
FavLyrics item = mPostItems.get(position);
|
||||||
|
cardTitle.setText(item.getTitle());
|
||||||
|
cardDescription.setText(item.getDescription());
|
||||||
|
// If no picture found, set default picture
|
||||||
|
if (item.getImgUrl().equals(SearchActivity.NO_IMG)) {
|
||||||
|
image.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
image.setVisibility(View.VISIBLE);
|
||||||
|
Glide.with(mContext)
|
||||||
|
.asBitmap()
|
||||||
|
.load(item.getImgUrl())
|
||||||
|
.into(image);
|
||||||
|
}
|
||||||
|
// click listener
|
||||||
|
parentLayout.setOnClickListener(view -> {
|
||||||
|
Intent intent = new Intent(mContext, ShowLyricsActivity.class);
|
||||||
|
intent.putExtra("song_url", item.getUrl());
|
||||||
|
intent.putExtra("title", item.getTitle());
|
||||||
|
intent.putExtra("description", item.getDescription());
|
||||||
|
intent.putExtra("img_url", item.getImgUrl());
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
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.jlyrics.BaseViewHolder;
|
||||||
|
import com.bernd32.jlyrics.Lyric;
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
import com.bernd32.jlyrics.ui.ArtistSongsActivity;
|
||||||
|
import com.bernd32.jlyrics.ui.SearchActivity;
|
||||||
|
import com.bernd32.jlyrics.ui.ShowLyricsActivity;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PostRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> {
|
||||||
|
private static final String TAG = "PostRecyclerAdapter";
|
||||||
|
private static final int VIEW_TYPE_LOADING = 0;
|
||||||
|
private static final int VIEW_TYPE_NORMAL = 1;
|
||||||
|
public static final int SONG_PAGE = 0;
|
||||||
|
public static final int ARTIST_LYRICS_PAGE = 1;
|
||||||
|
public static final int ARTISTS_PAGE = 2;
|
||||||
|
public static final int LYRICS_PAGE = 3;
|
||||||
|
private boolean isLoaderVisible = false;
|
||||||
|
private List<Lyric> mPostItems;
|
||||||
|
private Context mContext;
|
||||||
|
private int pageType;
|
||||||
|
|
||||||
|
public PostRecyclerAdapter(Context context, List<Lyric> postItems) {
|
||||||
|
Log.d(TAG, "PostRecyclerAdapter: constructor activated");
|
||||||
|
this.mPostItems = postItems;
|
||||||
|
this.mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
|
||||||
|
switch (viewType) {
|
||||||
|
case VIEW_TYPE_NORMAL:
|
||||||
|
return new ViewHolder(
|
||||||
|
LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem, parent, false));
|
||||||
|
case VIEW_TYPE_LOADING:
|
||||||
|
return new ProgressHolder(
|
||||||
|
LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
|
||||||
|
holder.onBind(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
if (isLoaderVisible) {
|
||||||
|
return position == mPostItems.size() - 1 ? VIEW_TYPE_LOADING : VIEW_TYPE_NORMAL;
|
||||||
|
} else {
|
||||||
|
return VIEW_TYPE_NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mPostItems == null ? 0 : mPostItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItems(List<Lyric> postItems) {
|
||||||
|
mPostItems.addAll(postItems);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLoading() {
|
||||||
|
isLoaderVisible = true;
|
||||||
|
mPostItems.add(new Lyric());
|
||||||
|
notifyItemInserted(mPostItems.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLoading() {
|
||||||
|
isLoaderVisible = false;
|
||||||
|
int position = mPostItems.size() - 1;
|
||||||
|
Lyric item = getItem(position);
|
||||||
|
if (item != null) {
|
||||||
|
mPostItems.remove(position);
|
||||||
|
notifyItemRemoved(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
Log.d(TAG, "clear: started");
|
||||||
|
// use it when performing new search
|
||||||
|
mPostItems.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
Glide.get(mContext).clearMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Lyric getItem(int position) {
|
||||||
|
return mPostItems.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPageType(int pageType) {
|
||||||
|
this.pageType = pageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends BaseViewHolder {
|
||||||
|
TextView cardTitle;
|
||||||
|
TextView cardDescription;
|
||||||
|
ImageView image;
|
||||||
|
MaterialCardView parentLayout;
|
||||||
|
|
||||||
|
ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
cardTitle = itemView.findViewById(R.id.card_title);
|
||||||
|
cardDescription = itemView.findViewById(R.id.card_description);
|
||||||
|
image = itemView.findViewById(R.id.image);
|
||||||
|
parentLayout = itemView.findViewById(R.id.parent_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void clear() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBind(int position) {
|
||||||
|
super.onBind(position);
|
||||||
|
Lyric item = mPostItems.get(position);
|
||||||
|
cardTitle.setText(item.getCardTitle());
|
||||||
|
cardDescription.setText(item.getCardDescription());
|
||||||
|
Log.d(TAG, "onBind: item#"+position+" img url="+ item.getImgUrl());
|
||||||
|
// If no picture found, set default picture
|
||||||
|
if (item.getImgUrl().equals(SearchActivity.NO_IMG)) {
|
||||||
|
image.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
image.setVisibility(View.VISIBLE);
|
||||||
|
Glide.with(mContext)
|
||||||
|
.asBitmap()
|
||||||
|
.load(item.getImgUrl())
|
||||||
|
.into(image);
|
||||||
|
}
|
||||||
|
// click listener
|
||||||
|
parentLayout.setOnClickListener(view -> {
|
||||||
|
Log.d(TAG, "onBind: " + pageType);
|
||||||
|
if (pageType == SONG_PAGE || pageType == ARTIST_LYRICS_PAGE) {
|
||||||
|
Intent intent = new Intent(mContext, ShowLyricsActivity.class);
|
||||||
|
intent.putExtra("song_url", item.getSongUrl());
|
||||||
|
intent.putExtra("title", item.getCardTitle());
|
||||||
|
intent.putExtra("description", item.getCardDescription());
|
||||||
|
intent.putExtra("img_url", item.getImgUrl());
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(mContext, ArtistSongsActivity.class);
|
||||||
|
intent.putExtra("url", item.getArtistUrl());
|
||||||
|
intent.putExtra("title", item.getCardTitle());
|
||||||
|
intent.putExtra("description", item.getCardDescription());
|
||||||
|
intent.putExtra("img_url", item.getImgUrl());
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ProgressHolder extends BaseViewHolder {
|
||||||
|
ProgressHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void clear() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.async;
|
||||||
|
|
||||||
|
public interface AsyncTaskListener<T> {
|
||||||
|
void onPreTask();
|
||||||
|
void onPostTask(T object);
|
||||||
|
void onFailure(Exception e, int statusCode);
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.async;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.utils.HelperClass;
|
||||||
|
|
||||||
|
import org.jsoup.HttpStatusException;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
public class GetDataAsyncTask extends AsyncTask<String, Void, Document> { // <[Input_Parameter Type], [Progress_Report Type], [Result Type]>
|
||||||
|
private static final String TAG = "GetDataAsyncTask";
|
||||||
|
private Exception exception;
|
||||||
|
private int httpStatusCode;
|
||||||
|
private AsyncTaskListener<Document> asyncTaskListener;
|
||||||
|
|
||||||
|
public GetDataAsyncTask(AsyncTaskListener<Document> asyncTaskListener) {
|
||||||
|
this.asyncTaskListener = asyncTaskListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
super.onCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
asyncTaskListener.onPreTask();
|
||||||
|
super.onPreExecute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Document doInBackground(String... params) {
|
||||||
|
String url = params[0];
|
||||||
|
Log.d(TAG, "doInBackground: loading url: " + url);
|
||||||
|
Document doc;
|
||||||
|
String userAgent = HelperClass.getUserAgent();
|
||||||
|
try {
|
||||||
|
doc = Jsoup
|
||||||
|
.connect(url)
|
||||||
|
.userAgent(userAgent)
|
||||||
|
.referrer("https://www.google.co.jp/")
|
||||||
|
.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof HttpStatusException) {
|
||||||
|
httpStatusCode = ((HttpStatusException) e).getStatusCode();
|
||||||
|
}
|
||||||
|
exception = e;
|
||||||
|
Log.d(TAG, "doInBackground: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return doc.outputSettings(new Document.OutputSettings().prettyPrint(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(Document s) {
|
||||||
|
super.onPostExecute(s);
|
||||||
|
if (asyncTaskListener != null) {
|
||||||
|
if (exception == null) {
|
||||||
|
asyncTaskListener.onPostTask(s);
|
||||||
|
} else {
|
||||||
|
asyncTaskListener.onFailure(exception, httpStatusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.async;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.utils.HelperClass;
|
||||||
|
|
||||||
|
import org.jsoup.Connection;
|
||||||
|
import org.jsoup.HttpStatusException;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for showing furigana
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PostDataAsyncTask extends AsyncTask<String, Void, Document> { // <[Input_Parameter Type], [Progress_Report Type], [Result Type]>
|
||||||
|
private static final String TAG = "PostDataAsyncTask";
|
||||||
|
private Exception exception;
|
||||||
|
private int httpStatusCode;
|
||||||
|
private AsyncTaskListener<String> listener;
|
||||||
|
|
||||||
|
public PostDataAsyncTask(AsyncTaskListener<String> listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
super.onCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
listener.onPreTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Document doInBackground(String... params) {
|
||||||
|
// params[0] is a lyric text
|
||||||
|
String url;
|
||||||
|
url = "https://www.jcinfo.net/ja/tools/kana";
|
||||||
|
Log.d(TAG, "doInBackground: params="+params[0]);
|
||||||
|
Document doc;
|
||||||
|
String userAgent = HelperClass.getUserAgent();
|
||||||
|
try {
|
||||||
|
|
||||||
|
Connection.Response response = Jsoup
|
||||||
|
.connect(url)
|
||||||
|
.method(Connection.Method.POST)
|
||||||
|
.userAgent(userAgent)
|
||||||
|
.referrer("https://www.google.co.jp/")
|
||||||
|
.data("txt", params[0])
|
||||||
|
.execute();
|
||||||
|
doc = response.parse();
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof HttpStatusException) {
|
||||||
|
httpStatusCode = ((HttpStatusException) e).getStatusCode();
|
||||||
|
}
|
||||||
|
exception = e;
|
||||||
|
Log.d(TAG, "doInBackground: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(Document s) {
|
||||||
|
String htmlString = s.select("#main-content > div.dsp2.radius_5").html();
|
||||||
|
Log.d(TAG, "onPostExecute: \n\n" + htmlString);
|
||||||
|
super.onPostExecute(s);
|
||||||
|
if (listener != null) {
|
||||||
|
if (exception == null) {
|
||||||
|
listener.onPostTask(htmlString);
|
||||||
|
} else {
|
||||||
|
listener.onFailure(exception, httpStatusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.async;
|
||||||
|
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.bernd32.romajihenkan.RomajiHenkan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converting a Japanese text to romaji.
|
||||||
|
* Since this operation is time-consuming, it should be performed in a non-UI thread
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RomanizeAsyncTask extends AsyncTask<String, Void, String> {
|
||||||
|
|
||||||
|
private AsyncTaskListener<String> listener;
|
||||||
|
|
||||||
|
public RomanizeAsyncTask(AsyncTaskListener<String> listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
listener.onPreTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String string) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onPostTask(string);
|
||||||
|
}
|
||||||
|
super.onPostExecute(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
super.onCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(String... strings) {
|
||||||
|
RomajiHenkan henkan = new RomajiHenkan();
|
||||||
|
return henkan.convert(strings[0]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.database;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.room.ColumnInfo;
|
||||||
|
import androidx.room.Entity;
|
||||||
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
|
@Entity(tableName = "lyrics")
|
||||||
|
public class FavLyrics {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@ColumnInfo(name = "title")
|
||||||
|
private String mTitle;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@ColumnInfo(name = "description")
|
||||||
|
private String mDescription;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@ColumnInfo(name = "url")
|
||||||
|
private String mUrl;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@ColumnInfo(name = "img_url")
|
||||||
|
private String mImgUrl;
|
||||||
|
|
||||||
|
@ColumnInfo(name = "time_stamp")
|
||||||
|
private long mTimeStamp;
|
||||||
|
|
||||||
|
public FavLyrics(@NonNull String title, @NonNull String description,
|
||||||
|
@NonNull String url, @NonNull String imgUrl, long timeStamp) {
|
||||||
|
this.mTitle = title;
|
||||||
|
this.mDescription = description;
|
||||||
|
this.mUrl = url;
|
||||||
|
this.mImgUrl = imgUrl;
|
||||||
|
this.mTimeStamp = timeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getTitle() {
|
||||||
|
return this.mTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getDescription() {
|
||||||
|
return this.mDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getUrl() {
|
||||||
|
return this.mUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getImgUrl() {
|
||||||
|
return this.mImgUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimeStamp() {return this.mTimeStamp; }
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.database;
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.room.Dao;
|
||||||
|
import androidx.room.Delete;
|
||||||
|
import androidx.room.Insert;
|
||||||
|
import androidx.room.OnConflictStrategy;
|
||||||
|
import androidx.room.Query;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
public interface LyricsDao {
|
||||||
|
|
||||||
|
// Notifies its active observers when the data has changed.
|
||||||
|
|
||||||
|
@Query("SELECT * from lyrics ORDER BY id DESC")
|
||||||
|
LiveData<List<FavLyrics>> getFavLyricsById();
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
void insert(FavLyrics lyrics);
|
||||||
|
|
||||||
|
@Query("DELETE FROM lyrics")
|
||||||
|
void deleteAll();
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
void deleteLyrics(FavLyrics lyrics);
|
||||||
|
|
||||||
|
@Query("SELECT count(*) FROM lyrics WHERE url = :url")
|
||||||
|
int isLyricsExists(String url);
|
||||||
|
|
||||||
|
@Query("DELETE FROM lyrics WHERE url = :url")
|
||||||
|
void deleteLyricsByUrl(String url);
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.database;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
class LyricsRepository {
|
||||||
|
|
||||||
|
private LyricsDao mLyricsDao;
|
||||||
|
private LiveData<List<FavLyrics>> mAllFavLyrics;
|
||||||
|
|
||||||
|
public LyricsRepository(Application application) {
|
||||||
|
LyricsRoomDatabase db = LyricsRoomDatabase.getDatabase(application);
|
||||||
|
mLyricsDao = db.lyricsDao();
|
||||||
|
mAllFavLyrics = mLyricsDao.getFavLyricsById();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Room executes all queries on a separate thread.
|
||||||
|
// Observed LiveData will notify the observer when the data has changed.
|
||||||
|
public LiveData<List<FavLyrics>> getAllLyrics() {
|
||||||
|
return mAllFavLyrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must call this on a non-UI thread
|
||||||
|
public void insert(FavLyrics lyrics) {
|
||||||
|
LyricsRoomDatabase.databaseWriteExecutor.execute(() -> {
|
||||||
|
mLyricsDao.insert(lyrics);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteAll() {
|
||||||
|
new DeleteAllLyricsAsyncTask(mLyricsDao).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to run off main thread
|
||||||
|
public void deleteLyrics(FavLyrics lyrics) {
|
||||||
|
new DeleteLyricsAsyncTask(mLyricsDao).execute(lyrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLyricsExists(String url) throws ExecutionException, InterruptedException {
|
||||||
|
new SearchLyricsAsyncTask(mLyricsDao).execute(url);
|
||||||
|
SearchLyricsAsyncTask searchLyricsAsyncTask = new SearchLyricsAsyncTask(mLyricsDao);
|
||||||
|
return searchLyricsAsyncTask.execute(url).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void deleteLyricsByUrl(String url) {
|
||||||
|
new DeleteLyricsByURLAsyncTask(mLyricsDao).execute(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Delete all favorites from the database (does not delete the table)
|
||||||
|
private static class DeleteAllLyricsAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
private LyricsDao mAsyncTaskDao;
|
||||||
|
|
||||||
|
DeleteAllLyricsAsyncTask(LyricsDao dao) {
|
||||||
|
mAsyncTaskDao = dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
mAsyncTaskDao.deleteAll();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a single favorite from the database.
|
||||||
|
|
||||||
|
private static class DeleteLyricsAsyncTask extends AsyncTask<FavLyrics, Void, Void> {
|
||||||
|
private LyricsDao mAsyncTaskDao;
|
||||||
|
|
||||||
|
DeleteLyricsAsyncTask(LyricsDao dao) {
|
||||||
|
mAsyncTaskDao = dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(final FavLyrics... params) {
|
||||||
|
mAsyncTaskDao.deleteLyrics(params[0]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SearchLyricsAsyncTask extends AsyncTask<String, Void, Boolean> {
|
||||||
|
private LyricsDao mAsyncTaskDao;
|
||||||
|
|
||||||
|
SearchLyricsAsyncTask(LyricsDao dao) {
|
||||||
|
mAsyncTaskDao = dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(final String... params) {
|
||||||
|
int count = mAsyncTaskDao.isLyricsExists(params[0]);
|
||||||
|
return count != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DeleteLyricsByURLAsyncTask extends AsyncTask<String, Void, Void> {
|
||||||
|
private LyricsDao mAsyncTaskDao;
|
||||||
|
|
||||||
|
DeleteLyricsByURLAsyncTask(LyricsDao dao) {
|
||||||
|
mAsyncTaskDao = dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(final String... params) {
|
||||||
|
mAsyncTaskDao.deleteLyricsByUrl(params[0]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.database;
|
||||||
|
|
||||||
|
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 java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
@Database(entities = {FavLyrics.class}, version = 3, exportSchema = false)
|
||||||
|
public abstract class LyricsRoomDatabase extends RoomDatabase {
|
||||||
|
|
||||||
|
abstract LyricsDao lyricsDao();
|
||||||
|
|
||||||
|
// marking the instance as volatile to ensure atomic access to the variable
|
||||||
|
private static volatile LyricsRoomDatabase INSTANCE;
|
||||||
|
private static final int NUMBER_OF_THREADS = 4;
|
||||||
|
static final ExecutorService databaseWriteExecutor =
|
||||||
|
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
|
||||||
|
|
||||||
|
static LyricsRoomDatabase getDatabase(final Context context) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
synchronized (LyricsRoomDatabase.class) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
|
||||||
|
LyricsRoomDatabase.class, "lyrics_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 RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onOpen(@NonNull SupportSQLiteDatabase db) {
|
||||||
|
super.onOpen(db);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.database;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View Model to keep a reference to the lyrics repository and
|
||||||
|
* an up-to-date list of all words.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class LyricsViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
|
private LyricsRepository mRepository;
|
||||||
|
// Using LiveData and caching what getFavLyricsById returns has several benefits:
|
||||||
|
// - We can put an observer on the data (instead of polling for changes) and only update the
|
||||||
|
// the UI when the data actually changes.
|
||||||
|
// - Repository is completely separated from the UI through the ViewModel.
|
||||||
|
private LiveData<List<FavLyrics>> mAllLyrics;
|
||||||
|
|
||||||
|
public LyricsViewModel(Application application) {
|
||||||
|
super(application);
|
||||||
|
mRepository = new LyricsRepository(application);
|
||||||
|
mAllLyrics = mRepository.getAllLyrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<List<FavLyrics>> getAllLyrics() {
|
||||||
|
return mAllLyrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(FavLyrics lyrics) {
|
||||||
|
mRepository.insert(lyrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteAll() {
|
||||||
|
mRepository.deleteAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteLyrics(FavLyrics lyrics) {
|
||||||
|
mRepository.deleteLyrics(lyrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLyricsExists(String url) throws ExecutionException, InterruptedException {
|
||||||
|
return mRepository.isLyricsExists(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteLyricsByUrl(String url) {
|
||||||
|
mRepository.deleteLyricsByUrl(url);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.bernd32.jlyrics.search;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.Lyric;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public interface CallbackInterface {
|
||||||
|
|
||||||
|
void showAlertDialog();
|
||||||
|
void setActivityTitle(int numberOfSongs);
|
||||||
|
void setPageType(int pageType);
|
||||||
|
void getSearchResults(ArrayList<Lyric> items);
|
||||||
|
void taskStarted();
|
||||||
|
void taskFinished();
|
||||||
|
void taskFailed(Exception e, int statusCode);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,230 @@
|
|||||||
|
package com.bernd32.jlyrics.search;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.Lyric;
|
||||||
|
import com.bernd32.jlyrics.adapters.PostRecyclerAdapter;
|
||||||
|
import com.bernd32.jlyrics.async.AsyncTaskListener;
|
||||||
|
import com.bernd32.jlyrics.async.GetDataAsyncTask;
|
||||||
|
|
||||||
|
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static com.bernd32.jlyrics.adapters.PostRecyclerAdapter.ARTISTS_PAGE;
|
||||||
|
import static com.bernd32.jlyrics.adapters.PostRecyclerAdapter.SONG_PAGE;
|
||||||
|
import static com.bernd32.jlyrics.ui.SearchActivity.NO_IMG;
|
||||||
|
|
||||||
|
class SearchLyricsRepository {
|
||||||
|
|
||||||
|
private static final String TAG = "SearchLyricsRepository";
|
||||||
|
private CallbackInterface listener;
|
||||||
|
private int pageType;
|
||||||
|
private int maxPage;
|
||||||
|
|
||||||
|
SearchLyricsRepository(Application application) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void newSearchRequest(String url) {
|
||||||
|
getDocumentAsyncTask(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Lyric> artistSearch(Document doc) {
|
||||||
|
// Handles artist search result
|
||||||
|
final ArrayList<Lyric> items = new ArrayList<>();
|
||||||
|
Elements imgElements = doc.select("#mnb > div.bdy");
|
||||||
|
Elements artists = doc.select("#mnb > div > p.mid > a");
|
||||||
|
Elements urls = doc.select("#mnb > div > p.mid > a");
|
||||||
|
Elements lyricsNumber = doc.select("#mnb > div > p.sml:contains(収録数:)");
|
||||||
|
int maxPages = 1;
|
||||||
|
// Show alert dialog if nothing found
|
||||||
|
if (artists.size() == 0) {
|
||||||
|
listener.showAlertDialog();
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < artists.size(); i++) {
|
||||||
|
Lyric postItem = new Lyric();
|
||||||
|
String getArtist = artists.get(i).text();
|
||||||
|
String getNumberOfSongs = lyricsNumber.get(i).text()
|
||||||
|
.replace("収録数:", "Lyrics: ")
|
||||||
|
.replace("曲", "");
|
||||||
|
String imgSrc = imgElements.get(i).select("a > img").attr("src");
|
||||||
|
if (!imgSrc.isEmpty()) {
|
||||||
|
postItem.setImgUrl(imgSrc);
|
||||||
|
} else {
|
||||||
|
postItem.setImgUrl(NO_IMG);
|
||||||
|
}
|
||||||
|
String artistUrl = urls.get(i).attr("href");
|
||||||
|
postItem.setCardTitle(getArtist);
|
||||||
|
postItem.setCardDescription(getNumberOfSongs);
|
||||||
|
postItem.setArtistUrl(artistUrl);
|
||||||
|
items.add(postItem);
|
||||||
|
}
|
||||||
|
listener.setActivityTitle(artists.size());
|
||||||
|
setMaxPage(maxPage);
|
||||||
|
}
|
||||||
|
listener.getSearchResults(items);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Lyric> songSearch(Document doc) {
|
||||||
|
Log.d(TAG, "songSearch: started");
|
||||||
|
final ArrayList<Lyric> items = new ArrayList<>();
|
||||||
|
maxPage = 1;
|
||||||
|
Elements imgElements = doc.select("#mnb > div.bdy");
|
||||||
|
Elements urls = doc.select("#mnb > div > p.mid > a");
|
||||||
|
Elements songNames = doc.select("#mnb > div.bdy > p.mid");
|
||||||
|
Elements artists = doc.select("#mnb > div.bdy > p.sml:contains(歌:)");
|
||||||
|
Element pageUrlElement = doc.select("#pager > a").first();
|
||||||
|
// Show alert dialog if nothing found
|
||||||
|
if (artists.size() == 0) {
|
||||||
|
listener.showAlertDialog();
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < artists.size(); i++) {
|
||||||
|
Lyric lyric = new Lyric();
|
||||||
|
String imgSrc = imgElements.get(i).select("a > img").attr("src");
|
||||||
|
if (!imgSrc.isEmpty()) {
|
||||||
|
lyric.setImgUrl(imgSrc);
|
||||||
|
} else {
|
||||||
|
lyric.setImgUrl(NO_IMG);
|
||||||
|
}
|
||||||
|
String url = urls.get(i).attr("href");
|
||||||
|
lyric.setSongUrl(url);
|
||||||
|
lyric.setCardTitle(songNames.get(i).text());
|
||||||
|
lyric.setCardDescription(artists.get(i).text().replace("歌:", ""));
|
||||||
|
items.add(lyric);
|
||||||
|
maxPage = numberOfPages(getNumberOfSongsFound(pageUrlElement));
|
||||||
|
setMaxPage(maxPage);
|
||||||
|
}
|
||||||
|
listener.setActivityTitle(getNumberOfSongsFound(pageUrlElement));
|
||||||
|
}
|
||||||
|
listener.getSearchResults(items);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getPageType(Document doc) {
|
||||||
|
String description = doc.select("meta[name=description]").first().attr("content");
|
||||||
|
Elements lyricsNumber = doc.select("#mnb > div > p.sml:contains(収録数:)");
|
||||||
|
// keywords for detecting a page type
|
||||||
|
final String search_result_keyword = "検索結果ページ";
|
||||||
|
final String lyrics_page_keyword = "歌詞ページ";
|
||||||
|
final String artist_list_keyword = "歌詞一覧ページ";
|
||||||
|
final String artist_search_result_keyword = "歌手名に";
|
||||||
|
if (description.contains(search_result_keyword) && lyricsNumber.isEmpty()) {
|
||||||
|
// Song search result (has pages)
|
||||||
|
Log.d(TAG, "getPageType: Search result page detected");
|
||||||
|
pageType = PostRecyclerAdapter.SONG_PAGE;
|
||||||
|
//adapter.setPageType(pageType);
|
||||||
|
listener.setPageType(pageType);
|
||||||
|
return pageType;
|
||||||
|
|
||||||
|
} else if (description.contains(lyrics_page_keyword)) {
|
||||||
|
Log.d(TAG, "getPageType: Lyrics page detected");
|
||||||
|
pageType = PostRecyclerAdapter.LYRICS_PAGE;
|
||||||
|
//adapter.setPageType(pageType);
|
||||||
|
return pageType;
|
||||||
|
} else if (description.contains(artist_list_keyword)) {
|
||||||
|
Log.d(TAG, "getPageType: Artist list page detected");
|
||||||
|
pageType = PostRecyclerAdapter.ARTIST_LYRICS_PAGE;
|
||||||
|
listener.setPageType(pageType);
|
||||||
|
return pageType;
|
||||||
|
} else if(description.contains(artist_search_result_keyword) && !lyricsNumber.isEmpty()) {
|
||||||
|
Log.d(TAG, "getPageType: Artist search result page detected");
|
||||||
|
pageType = PostRecyclerAdapter.ARTISTS_PAGE;
|
||||||
|
listener.setPageType(pageType);
|
||||||
|
return pageType;
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "getPageType: cannot detect page type");
|
||||||
|
return PostRecyclerAdapter.SONG_PAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getDocumentAsyncTask(String url) {
|
||||||
|
Log.d(TAG, "startAsyncTask: " + url);
|
||||||
|
GetDataAsyncTask task = new GetDataAsyncTask(new AsyncTaskListener<Document>() {
|
||||||
|
@Override
|
||||||
|
public void onPreTask() {
|
||||||
|
listener.taskStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostTask(Document doc) {
|
||||||
|
pageType = getPageType(doc);
|
||||||
|
// Get lyrics from a song search type
|
||||||
|
if (pageType == SONG_PAGE) {
|
||||||
|
songSearch(doc);
|
||||||
|
} else if (pageType == ARTISTS_PAGE) {
|
||||||
|
artistSearch(doc);
|
||||||
|
} else {
|
||||||
|
artistSongList(doc);
|
||||||
|
}
|
||||||
|
listener.taskFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e, int statusCode) {
|
||||||
|
listener.taskFailed(e, statusCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.execute(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Lyric> artistSongList(Document doc) {
|
||||||
|
// Handles artist's list of lyrics
|
||||||
|
final ArrayList<Lyric> items = new ArrayList<>();
|
||||||
|
Elements imgElements = doc.select("#mnb > div.cnt > div[id^=ly]");
|
||||||
|
Elements titles = doc.select("#mnb > div.cnt > div[id^=ly] > p.ttl > a");
|
||||||
|
String description = doc.selectFirst("#mnb > div.cnt > div.cap > h2").text();
|
||||||
|
for (int i = 0; i < titles.size(); i++) {
|
||||||
|
Lyric postItem = new Lyric();
|
||||||
|
String imgSrc = imgElements.get(i).select("a > img.i5r").attr("src");
|
||||||
|
if (!imgSrc.isEmpty()) {
|
||||||
|
Log.d(TAG, i + " = " + imgSrc);
|
||||||
|
postItem.setImgUrl(imgSrc);
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "artistDocumentHandle: no image found");
|
||||||
|
postItem.setImgUrl(NO_IMG);
|
||||||
|
}
|
||||||
|
String title = titles.get(i).text();
|
||||||
|
String url = titles.get(i).attr("href");
|
||||||
|
postItem.setCardTitle(title);
|
||||||
|
postItem.setCardDescription(description.replace("の歌詞リスト", "")); // leave blank for now
|
||||||
|
postItem.setSongUrl("http://j-lyric.net" + url);
|
||||||
|
Log.d(TAG, "documentHandler: url = http://j-lyric.net" + url);
|
||||||
|
items.add(postItem);
|
||||||
|
}
|
||||||
|
listener.setActivityTitle(titles.size());
|
||||||
|
listener.getSearchResults(items);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getNumberOfSongsFound(Element pageUrlElement) {
|
||||||
|
String url = pageUrlElement.attr("href");
|
||||||
|
Uri uri = Uri.parse(url);
|
||||||
|
String c = uri.getQueryParameter("c");
|
||||||
|
assert c != null;
|
||||||
|
return Integer.valueOf(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int numberOfPages(int songs) {
|
||||||
|
double p = Math.ceil((double) songs/20);
|
||||||
|
return (int) p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(CallbackInterface listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxPage(int maxPage) {
|
||||||
|
this.maxPage = maxPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxPage() {
|
||||||
|
return maxPage;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.bernd32.jlyrics.search;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
|
||||||
|
public class SearchLyricsViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
|
private SearchLyricsRepository mRepository;
|
||||||
|
private static final String TAG = "SearchLyricsViewModel";
|
||||||
|
|
||||||
|
public SearchLyricsViewModel(Application application) {
|
||||||
|
super(application);
|
||||||
|
mRepository = new SearchLyricsRepository(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void newSearchRequest(String url) {
|
||||||
|
Log.d(TAG, "newSearchRequest: " + url);
|
||||||
|
mRepository.newSearchRequest(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(CallbackInterface callback) {
|
||||||
|
mRepository.addListener(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxPage() {
|
||||||
|
Log.d(TAG, "getMaxPage: " + mRepository.getMaxPage());
|
||||||
|
return mRepository.getMaxPage();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,174 @@
|
|||||||
|
package com.bernd32.jlyrics.ui;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.Lyric;
|
||||||
|
import com.bernd32.jlyrics.PaginationListener;
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
import com.bernd32.jlyrics.adapters.PostRecyclerAdapter;
|
||||||
|
import com.bernd32.jlyrics.async.GetDataAsyncTask;
|
||||||
|
import com.bernd32.jlyrics.search.CallbackInterface;
|
||||||
|
import com.bernd32.jlyrics.search.SearchLyricsViewModel;
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class ArtistSongsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final String TAG = "SearchActivity";
|
||||||
|
private static final String NO_IMG = "no_img";
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public RecyclerView mRecyclerView;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private PostRecyclerAdapter adapter;
|
||||||
|
private String url;
|
||||||
|
private FloatingActionButton floatingActionButton;
|
||||||
|
private ExtendedFloatingActionButton cancelFAB;
|
||||||
|
private GetDataAsyncTask task;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreate: started");
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_search);
|
||||||
|
cancelFAB = findViewById(R.id.load_cancel);
|
||||||
|
progressBar = findViewById(R.id.progress_bar);
|
||||||
|
mRecyclerView = findViewById(R.id.recyclerv_view);
|
||||||
|
floatingActionButton = findViewById(R.id.floating_action_button);
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
assert actionBar != null;
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
Bundle extras = getIntent().getExtras();
|
||||||
|
if (extras != null) {
|
||||||
|
loadIntents();
|
||||||
|
}
|
||||||
|
mRecyclerView.setHasFixedSize(true);
|
||||||
|
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||||
|
mRecyclerView.setLayoutManager(layoutManager);
|
||||||
|
adapter = new PostRecyclerAdapter(this, new ArrayList<>());
|
||||||
|
mRecyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
|
SearchLyricsViewModel viewModel = new ViewModelProvider(this).get(SearchLyricsViewModel.class);
|
||||||
|
// show progressbar only in initial loading
|
||||||
|
CallbackInterface listener = new CallbackInterface() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showAlertDialog() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setActivityTitle(int numberOfSongs) {
|
||||||
|
ArtistSongsActivity.this.setTitle(getString(R.string.title_found) +
|
||||||
|
" " + numberOfSongs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPageType(int pageType) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getSearchResults(ArrayList<Lyric> items) {
|
||||||
|
adapter.addItems(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void taskStarted() {
|
||||||
|
Log.d(TAG, "onPreTask: started");
|
||||||
|
// show progressbar only in initial loading
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
cancelFAB.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void taskFinished() {
|
||||||
|
Log.d(TAG, "onPostTask: started");
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
cancelFAB.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void taskFailed(Exception e, int statusCode) {
|
||||||
|
Toast.makeText(ArtistSongsActivity.this,
|
||||||
|
getString(R.string.connection_failed),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
Log.d(TAG, "onFailure: " + e.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
viewModel.addListener(listener);
|
||||||
|
viewModel.newSearchRequest(url);
|
||||||
|
|
||||||
|
floatingActionButton.setOnClickListener(view -> {
|
||||||
|
mRecyclerView.smoothScrollToPosition(0);
|
||||||
|
});
|
||||||
|
mRecyclerView.addOnScrollListener(new PaginationListener(layoutManager) {
|
||||||
|
@Override
|
||||||
|
protected void loadMoreItems() {
|
||||||
|
floatingActionButton.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLastPage() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLoading() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showFAB() {
|
||||||
|
if(floatingActionButton.getVisibility() != View.VISIBLE) {
|
||||||
|
floatingActionButton.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void hideFAB() {
|
||||||
|
if(floatingActionButton.getVisibility() == View.VISIBLE) {
|
||||||
|
floatingActionButton.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp(){
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoadCancel(View view) {
|
||||||
|
if (task != null)
|
||||||
|
task.cancel(true);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadIntents() {
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String title = intent.getStringExtra("title");
|
||||||
|
String description = intent.getStringExtra("description");
|
||||||
|
url = intent.getStringExtra("url");
|
||||||
|
String imgUrl = intent.getStringExtra("img_url");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.ui;
|
||||||
|
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
class ChangeFontDialogFragment extends DialogFragment {
|
||||||
|
private static final String TAG = "ChangeFontDialogFragmen";
|
||||||
|
private OnDialogButtonClick listener;
|
||||||
|
|
||||||
|
public void setOnDialogButtonClick(OnDialogButtonClick buttonClick) {
|
||||||
|
this.listener = buttonClick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
// Instantiate the NoticeDialogListener so we can send events to the host
|
||||||
|
listener = (OnDialogButtonClick) context;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreate: started");
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreateView: started");
|
||||||
|
return inflater.inflate(R.layout.dialog_change_fontsize, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onViewCreated: started");
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreateDialog: started");
|
||||||
|
// Use the Builder class for convenient dialog construction
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(Objects.requireNonNull(getActivity()));
|
||||||
|
// Get the layout inflater
|
||||||
|
LayoutInflater inflater = requireActivity().getLayoutInflater();
|
||||||
|
// Inflate and set the layout for the dialog
|
||||||
|
// Pass null as the parent view because its going in the dialog layout
|
||||||
|
@SuppressLint("InflateParams") View content = inflater.inflate(R.layout.dialog_change_fontsize, null);
|
||||||
|
builder.setView(content);
|
||||||
|
final TextView fontSizeTV = content.findViewById(R.id.font_size_textview);
|
||||||
|
final SeekBar seekBar = content.findViewById(R.id.seek_bar);
|
||||||
|
TextView lyricsTV = ((ShowLyricsActivity) getActivity()).lyricsTV;
|
||||||
|
// Convert received font size from px to sp metrics
|
||||||
|
float px = lyricsTV.getTextSize();
|
||||||
|
float sp = px / getResources().getDisplayMetrics().scaledDensity;
|
||||||
|
seekBar.setProgress((int) sp);
|
||||||
|
fontSizeTV.setText(String.valueOf((int) sp));
|
||||||
|
builder.setMessage(R.string.change_font)
|
||||||
|
.setPositiveButton(R.string.ok, (dialog, id) -> {
|
||||||
|
listener.onOkClicked(ChangeFontDialogFragment.this);
|
||||||
|
// Send data (seekBar.getProgress()) through the interface
|
||||||
|
// (OnDialogButtonClick.changeFontSize()) back to the activity (ShowLyricsActivty)
|
||||||
|
listener.changeFontSize(seekBar.getProgress());
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, (dialog, id) -> {
|
||||||
|
// Send the negative button event back to the host activity
|
||||||
|
listener.onCancelClicked(ChangeFontDialogFragment.this);
|
||||||
|
});
|
||||||
|
|
||||||
|
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
fontSizeTV.setText(" " + progress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Create the AlertDialog object and return it
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnDialogButtonClick {
|
||||||
|
void onOkClicked(DialogFragment dialog);
|
||||||
|
void onCancelClicked(DialogFragment dialog);
|
||||||
|
void changeFontSize(int i);
|
||||||
|
}
|
||||||
|
}
|
123
app/src/main/java/com/bernd32/jlyrics/ui/FavoritesActivity.java
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.ui;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
import com.bernd32.jlyrics.adapters.FavLyricsAdapter;
|
||||||
|
import com.bernd32.jlyrics.database.FavLyrics;
|
||||||
|
import com.bernd32.jlyrics.database.LyricsViewModel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FavoritesActivity extends AppCompatActivity {
|
||||||
|
private static final String TAG = "FavoritesActivity";
|
||||||
|
private LyricsViewModel lyricsViewModel;
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public RecyclerView recyclerView; // must not be private or static
|
||||||
|
private FavLyricsAdapter adapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreate: has been called");
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_favorites);
|
||||||
|
recyclerView = findViewById(R.id.recycler_view_favs);
|
||||||
|
TextView emptyMsg = findViewById(R.id.empty_message);
|
||||||
|
// Find the toolbar view inside the activity layout
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
assert actionBar != null;
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
recyclerView.setHasFixedSize(true);
|
||||||
|
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||||
|
recyclerView.setLayoutManager(layoutManager);
|
||||||
|
adapter = new FavLyricsAdapter(this, new ArrayList<>());
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
lyricsViewModel = new ViewModelProvider(this).get(LyricsViewModel.class);
|
||||||
|
// Get all the lyrics from the database
|
||||||
|
// and associate them with the adapter
|
||||||
|
lyricsViewModel.getAllLyrics().observe(this, (List<FavLyrics> lyrics) -> {
|
||||||
|
// Update the cached copy of the lyrics in the adapter.
|
||||||
|
adapter.addItems(lyrics);
|
||||||
|
// Show a message if the list is empty
|
||||||
|
emptyMsg.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
deleteFavoriteBySwipe();
|
||||||
|
FavoritesActivity.this.setTitle(getString(R.string.favs));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteFavoriteBySwipe() {
|
||||||
|
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();
|
||||||
|
FavLyrics myLyrics = adapter.getItem(position);
|
||||||
|
Toast.makeText(FavoritesActivity.this,
|
||||||
|
getString(R.string.delete_lyrics_preamble), Toast.LENGTH_LONG).show();
|
||||||
|
lyricsViewModel.deleteLyrics(myLyrics);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Attach the item touch helper to the recycler view
|
||||||
|
helper.attachToRecyclerView(recyclerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_favorites, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.delete_all) {
|
||||||
|
lyricsViewModel.deleteAll();
|
||||||
|
adapter.clear();
|
||||||
|
Toast.makeText(this, "Cleared", Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
171
app/src/main/java/com/bernd32/jlyrics/ui/FuriganaActivity.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.ui;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.webkit.WebSettings;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
import com.bernd32.jlyrics.async.AsyncTaskListener;
|
||||||
|
import com.bernd32.jlyrics.async.PostDataAsyncTask;
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
|
||||||
|
|
||||||
|
public class FuriganaActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final String TAG = "FuriganaActivity";
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private ExtendedFloatingActionButton cancelFAB;
|
||||||
|
private PostDataAsyncTask task;
|
||||||
|
private String savedHtmlString = "";
|
||||||
|
private boolean darkTheme;
|
||||||
|
private float fontSize;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_furigana);
|
||||||
|
progressBar = findViewById(R.id.progress_bar);
|
||||||
|
cancelFAB = findViewById(R.id.load_cancel);
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
assert actionBar != null;
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String lyrics = intent.getStringExtra("lyrics_text");
|
||||||
|
darkTheme = intent.getBooleanExtra("theme", false);
|
||||||
|
fontSize = intent.getFloatExtra("font_size", 20);
|
||||||
|
// Load lyrics from savedInstanceState, so we don't have to call startAsyncTask
|
||||||
|
// if activity is stopped or destroyed
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
startAsyncTask(lyrics);
|
||||||
|
} else {
|
||||||
|
savedHtmlString = savedInstanceState.getString("saved_lyrics");
|
||||||
|
startWebView(savedHtmlString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
Log.d(TAG, "onSaveInstanceState: ");
|
||||||
|
if (!savedHtmlString.isEmpty()) {
|
||||||
|
outState.putString("saved_lyrics", savedHtmlString);
|
||||||
|
}
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startAsyncTask(String textToConvert) {
|
||||||
|
Log.d(TAG, "startAsyncTask: started");
|
||||||
|
task = new PostDataAsyncTask(new AsyncTaskListener<String>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPreTask() {
|
||||||
|
Log.d(TAG, "onPreTask: started");
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
cancelFAB.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostTask(String htmlString) {
|
||||||
|
Log.d(TAG, "onPostTask: started");
|
||||||
|
// Save a string from AsyncTask to the local variable
|
||||||
|
savedHtmlString = htmlString;
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
cancelFAB.setVisibility(View.GONE);
|
||||||
|
startWebView(htmlString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e, int statusCode) {
|
||||||
|
Toast.makeText(FuriganaActivity.this,
|
||||||
|
getString(R.string.connection_failed),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
// TODO: 24.11.2019 log Exception e and int statusCode
|
||||||
|
Log.d(TAG, "onFailure: " + e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.execute(textToConvert);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoadCancel(View view) {
|
||||||
|
if (task != null)
|
||||||
|
task.cancel(true);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startWebView(String htmlString) {
|
||||||
|
WebView webView = findViewById(R.id.webview);
|
||||||
|
WebSettings webSettings = webView.getSettings();
|
||||||
|
webSettings.setTextZoom(120);
|
||||||
|
webSettings.setSupportZoom(true);
|
||||||
|
webSettings.setBuiltInZoomControls(true);
|
||||||
|
webSettings.setDisplayZoomControls(false);
|
||||||
|
String html = setUpHtmlString(htmlString, (int)fontSize, darkTheme);
|
||||||
|
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
|
||||||
|
webView.loadData(html,"text/html", "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String setUpHtmlString(String htmlString, int fontSize, boolean darkTheme) {
|
||||||
|
String bgColor = darkTheme ? "black":"white";
|
||||||
|
String fontColor = darkTheme ? "white":"black";
|
||||||
|
String html = String.format("<html><head><style>"
|
||||||
|
+ "span {color:%s}"
|
||||||
|
+ "</style></head><body style=\"background-color:%s\">"
|
||||||
|
+ "%s"
|
||||||
|
+ "</body></html>",fontColor, bgColor, htmlString);
|
||||||
|
Log.d(TAG, "setUpHtmlString: "+html);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
|
return super.onPrepareOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
Log.d(TAG, "onCreateOptionsMenu: started");
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp(){
|
||||||
|
if (task != null)
|
||||||
|
task.cancel(true);
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
214
app/src/main/java/com/bernd32/jlyrics/ui/LauncherActivity.java
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.constraintlayout.widget.Group;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
import com.bernd32.jlyrics.utils.PreferencesManager;
|
||||||
|
|
||||||
|
public class LauncherActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final String TAG = "LauncherActivity";
|
||||||
|
public static final String SEARCH_QUERY_SONG = "com.bernd32.jlyrics.SEARCH_QUERY_SONG";
|
||||||
|
public static final String SPINNER_LIST = "com.bernd32.jlyrics.SPINNER_LIST";
|
||||||
|
public static final String ARTIST_INPUT = "com.bernd32.jlyrics.ARTIST_INPUT";
|
||||||
|
public static final String SONG_INPUT = "com.bernd32.jlyrics.SONG_INPUT";
|
||||||
|
public static final String LYRICS_INPUT = "com.bernd32.jlyrics.LYRICS_INPUT";
|
||||||
|
public static final String ARTIST_SPINNER = "com.bernd32.jlyrics.ARTIST_SPINNER";
|
||||||
|
public static final String SONG_SPINNER = "com.bernd32.jlyrics.SONG_SPINNER";
|
||||||
|
public static final String LYRICS_SPINNER = "com.bernd32.jlyrics.LYRICS_SPINNER";
|
||||||
|
public static final String IGNORE_MAIN_SEARCH_INPUT = "com.bernd32.jlyrics.IGNORE_MAIN_SEARCH_INPUT";
|
||||||
|
|
||||||
|
private EditText mainSearchInput;
|
||||||
|
private EditText songInput;
|
||||||
|
private EditText artistInput;
|
||||||
|
private EditText lyricsInput;
|
||||||
|
private Spinner songSpinner;
|
||||||
|
private Spinner artistSpinner;
|
||||||
|
private Spinner lyricsSpinner;
|
||||||
|
private Spinner spinner;
|
||||||
|
private Button detailedSearchButton;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
mainSearchInput = findViewById(R.id.search_input);
|
||||||
|
spinner = findViewById(R.id.spinner);
|
||||||
|
songSpinner = findViewById(R.id.song_spinner);
|
||||||
|
artistSpinner = findViewById(R.id.artist_spinner);
|
||||||
|
lyricsSpinner = findViewById(R.id.lyrics_spinner);
|
||||||
|
songInput = findViewById(R.id.song_input);
|
||||||
|
artistInput = findViewById(R.id.artist_input);
|
||||||
|
lyricsInput = findViewById(R.id.lyrics_input);
|
||||||
|
detailedSearchButton = findViewById(R.id.detailed_search_button);
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
// Create an ArrayAdapter using the string array and a default spinner layout
|
||||||
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
|
||||||
|
R.array.spinner_items, android.R.layout.simple_spinner_item);
|
||||||
|
ArrayAdapter<CharSequence> adapter_d = ArrayAdapter.createFromResource(this,
|
||||||
|
R.array.spinner_search_options, android.R.layout.simple_spinner_item);
|
||||||
|
ArrayAdapter<CharSequence> adapter_l = ArrayAdapter.createFromResource(this,
|
||||||
|
R.array.lyrics_spinner_options, android.R.layout.simple_spinner_item);
|
||||||
|
// Specify the layout to use when the list of choices appears
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
adapter_d.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
adapter_l.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
// Apply the adapter to the spinner
|
||||||
|
spinner.setAdapter(adapter);
|
||||||
|
songSpinner.setAdapter(adapter_d);
|
||||||
|
artistSpinner.setAdapter(adapter_d);
|
||||||
|
lyricsSpinner.setAdapter(adapter_l);
|
||||||
|
// Set default values to the spinners
|
||||||
|
songSpinner.setSelection(2);
|
||||||
|
artistSpinner.setSelection(2);
|
||||||
|
lyricsSpinner.setSelection(1);
|
||||||
|
// Load theme settings from PrefManager
|
||||||
|
AppCompatDelegate.setDefaultNightMode(
|
||||||
|
AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
PreferencesManager.initializeInstance(this);
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
if (pm.getDarkThemeSelected()) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
|
} else {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
getMenuInflater().inflate(R.menu.menu_main, menu);
|
||||||
|
// Get saved data and set value to the checkable menu item
|
||||||
|
MenuItem darkThemeItem = menu.findItem(R.id.dark_theme);
|
||||||
|
PreferencesManager.initializeInstance(this);
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
darkThemeItem.setChecked(pm.getDarkThemeSelected());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.toolbar_show_favs:
|
||||||
|
Intent intent = new Intent(this, FavoritesActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
case R.id.quit:
|
||||||
|
this.finishAffinity();
|
||||||
|
return true;
|
||||||
|
case R.id.contact:
|
||||||
|
sendMail(getString(R.string.email_address), getString(R.string.email_subject_question));
|
||||||
|
case R.id.dark_theme:
|
||||||
|
item.setChecked(!item.isChecked());
|
||||||
|
// Save to value
|
||||||
|
PreferencesManager.initializeInstance(this);
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
pm.setDarkThemeSelected(item.isChecked());
|
||||||
|
if (item.isChecked()) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
|
} else {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMail(String email, String subject) {
|
||||||
|
String[] email_address = new String[] {email};
|
||||||
|
Intent mailto = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", email, null));
|
||||||
|
mailto.putExtra(Intent.EXTRA_EMAIL, email_address);
|
||||||
|
mailto.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||||
|
startActivity(Intent.createChooser(mailto, "Send E-mail"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoadLyrics(View view) {
|
||||||
|
Intent intent = new Intent(this, SearchActivity.class);
|
||||||
|
boolean ignoreMainSearchInput = true;
|
||||||
|
String mainSearchText = mainSearchInput.getText().toString().trim();
|
||||||
|
int spinnerPos = spinner.getSelectedItemPosition();
|
||||||
|
String artistText = artistInput.getText().toString().trim();
|
||||||
|
String songText = songInput.getText().toString().trim();
|
||||||
|
String lyricsText = lyricsInput.getText().toString().trim();
|
||||||
|
int artistSpinnerPos = artistSpinner.getSelectedItemPosition();
|
||||||
|
int songSpinnerPos = songSpinner.getSelectedItemPosition();
|
||||||
|
int lyricsSpinnerPos = lyricsSpinner.getSelectedItemPosition();
|
||||||
|
if (artistText.isEmpty() && songText.isEmpty() && lyricsText.isEmpty()) {
|
||||||
|
ignoreMainSearchInput = false;
|
||||||
|
}
|
||||||
|
if (!mainSearchText.isEmpty() && !ignoreMainSearchInput) {
|
||||||
|
Log.d(TAG, "onLoadLyrics: user didn't use detailed search");
|
||||||
|
intent.putExtra(SEARCH_QUERY_SONG, mainSearchText);
|
||||||
|
intent.putExtra(SPINNER_LIST, spinnerPos);
|
||||||
|
}
|
||||||
|
if (ignoreMainSearchInput){
|
||||||
|
Log.d(TAG, "onLoadLyrics: user used detailed search");
|
||||||
|
intent.putExtra(ARTIST_INPUT, artistText);
|
||||||
|
intent.putExtra(SONG_INPUT, songText);
|
||||||
|
intent.putExtra(LYRICS_INPUT, lyricsText);
|
||||||
|
intent.putExtra(ARTIST_SPINNER, artistSpinnerPos);
|
||||||
|
intent.putExtra(SONG_SPINNER, songSpinnerPos);
|
||||||
|
intent.putExtra(LYRICS_SPINNER, lyricsSpinnerPos);
|
||||||
|
}
|
||||||
|
if (intent.resolveActivity(getPackageManager()) != null &&
|
||||||
|
(!mainSearchText.isEmpty() || ignoreMainSearchInput)) {
|
||||||
|
intent.putExtra(IGNORE_MAIN_SEARCH_INPUT, ignoreMainSearchInput);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDetailedSearch(View view) {
|
||||||
|
Group group = findViewById(R.id.group);
|
||||||
|
if (group.getVisibility() == View.GONE) {
|
||||||
|
// Hide keyboard
|
||||||
|
view = this.getCurrentFocus();
|
||||||
|
if (view != null) {
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
assert imm != null;
|
||||||
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
group.setVisibility(View.VISIBLE);
|
||||||
|
detailedSearchButton.setText(getString(R.string.detailed_search_hide));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
group.setVisibility(View.GONE);
|
||||||
|
detailedSearchButton.setText(getString(R.string.detailed_search_show));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
322
app/src/main/java/com/bernd32/jlyrics/ui/SearchActivity.java
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.ui;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.PaginationListener;
|
||||||
|
import com.bernd32.jlyrics.Lyric;
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
import com.bernd32.jlyrics.adapters.PostRecyclerAdapter;
|
||||||
|
import com.bernd32.jlyrics.async.GetDataAsyncTask;
|
||||||
|
import com.bernd32.jlyrics.search.CallbackInterface;
|
||||||
|
import com.bernd32.jlyrics.search.SearchLyricsViewModel;
|
||||||
|
import com.bernd32.jlyrics.utils.PreferencesManager;
|
||||||
|
import com.bernd32.romajihenkan.RomajiHenkan;
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static com.bernd32.jlyrics.PaginationListener.PAGE_START;
|
||||||
|
import static com.bernd32.jlyrics.utils.HelperClass.urlBuilder;
|
||||||
|
|
||||||
|
public class SearchActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final String TAG = "SearchActivity";
|
||||||
|
public static final String NO_IMG = "no_img";
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public RecyclerView mRecyclerView; // must not be private or static
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private PostRecyclerAdapter adapter;
|
||||||
|
private int currentPage = PAGE_START;
|
||||||
|
private boolean isLastPage = false;
|
||||||
|
private boolean isLoading = false;
|
||||||
|
private String searchInput;
|
||||||
|
private int spinnerPos;
|
||||||
|
private String artistText;
|
||||||
|
private String songText;
|
||||||
|
private String lyricsText;
|
||||||
|
private int artistSpinnerPos;
|
||||||
|
private int songSpinnerPos;
|
||||||
|
private int lyricsSpinnerPos;
|
||||||
|
private boolean ignoreMainSearch;
|
||||||
|
private FloatingActionButton floatingActionButton;
|
||||||
|
private ExtendedFloatingActionButton cancelFAB;
|
||||||
|
private GetDataAsyncTask task;
|
||||||
|
private SearchLyricsViewModel viewModel;
|
||||||
|
private int maxPage;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
// Get intents
|
||||||
|
Bundle extras = getIntent().getExtras();
|
||||||
|
if (extras != null) {
|
||||||
|
intentLoader();
|
||||||
|
}
|
||||||
|
// UI
|
||||||
|
setContentView(R.layout.activity_search);
|
||||||
|
progressBar = findViewById(R.id.progress_bar);
|
||||||
|
mRecyclerView = findViewById(R.id.recyclerv_view);
|
||||||
|
floatingActionButton = findViewById(R.id.floating_action_button);
|
||||||
|
cancelFAB = findViewById(R.id.load_cancel);
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
assert actionBar != null;
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
mRecyclerView.setHasFixedSize(true);
|
||||||
|
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||||
|
mRecyclerView.setLayoutManager(layoutManager);
|
||||||
|
adapter = new PostRecyclerAdapter(this, new ArrayList<>());
|
||||||
|
mRecyclerView.setAdapter(adapter);
|
||||||
|
// initial load
|
||||||
|
String url = ignoreMainSearch ?
|
||||||
|
urlBuilder(PAGE_START, artistText, artistSpinnerPos, songText,
|
||||||
|
songSpinnerPos, lyricsText, lyricsSpinnerPos) :
|
||||||
|
urlBuilder(PAGE_START, spinnerPos, searchInput);
|
||||||
|
viewModel = new ViewModelProvider(this).get(SearchLyricsViewModel.class);
|
||||||
|
CallbackInterface listener = new CallbackInterface() {
|
||||||
|
|
||||||
|
// Show alert dialog in case when no search results were found
|
||||||
|
@Override
|
||||||
|
public void showAlertDialog() {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(SearchActivity.this);
|
||||||
|
builder.setTitle(getString(R.string.not_found_title));
|
||||||
|
builder.setMessage(getString(R.string.not_found_message));
|
||||||
|
builder.setPositiveButton(R.string.ok, (dialog, id) -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
AlertDialog dialog = builder.create();
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show number of search result found in the action bar
|
||||||
|
@Override
|
||||||
|
public void setActivityTitle(int numberOfSongs) {
|
||||||
|
SearchActivity.this.setTitle(getString(R.string.title_found) +
|
||||||
|
" " + numberOfSongs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell our adapter the search result type we got, so adapter's click listener
|
||||||
|
// will know what activity show be opened when we click on a search result
|
||||||
|
@Override
|
||||||
|
public void setPageType(int pageType) {
|
||||||
|
adapter.setPageType(pageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is basically the main callback method that takes the search
|
||||||
|
// result items from the view model and loads them into the UI via our recycler adapter
|
||||||
|
// This callback method also handles the pagination future, and loads new search
|
||||||
|
// result pages into our adapter
|
||||||
|
@Override
|
||||||
|
public void getSearchResults(ArrayList<Lyric> items) {
|
||||||
|
maxPage = viewModel.getMaxPage();
|
||||||
|
newPageLoader(maxPage, items);
|
||||||
|
/*
|
||||||
|
* add scroll listener while user reach in bottom load more will call
|
||||||
|
*/
|
||||||
|
mRecyclerView.addOnScrollListener(new PaginationListener(layoutManager) {
|
||||||
|
@Override
|
||||||
|
protected void loadMoreItems() {
|
||||||
|
isLoading = true;
|
||||||
|
currentPage++;
|
||||||
|
String url = ignoreMainSearch ?
|
||||||
|
urlBuilder(currentPage, artistText, artistSpinnerPos, songText,
|
||||||
|
songSpinnerPos, lyricsText, lyricsSpinnerPos) :
|
||||||
|
urlBuilder(currentPage, spinnerPos, searchInput);
|
||||||
|
floatingActionButton.setVisibility(View.VISIBLE);
|
||||||
|
// Load search results from a new page
|
||||||
|
viewModel.newSearchRequest(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLastPage() {
|
||||||
|
return isLastPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLoading() {
|
||||||
|
return isLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showFAB() {
|
||||||
|
if (floatingActionButton.getVisibility() != View.VISIBLE) {
|
||||||
|
floatingActionButton.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void hideFAB() {
|
||||||
|
if (floatingActionButton.getVisibility() == View.VISIBLE) {
|
||||||
|
floatingActionButton.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Showing loading animation when user started searching
|
||||||
|
@Override
|
||||||
|
public void taskStarted() {
|
||||||
|
if (currentPage == 1) {
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
mRecyclerView.setVisibility(View.GONE);
|
||||||
|
cancelFAB.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide loading animation and show search results
|
||||||
|
@Override
|
||||||
|
public void taskFinished() {
|
||||||
|
Log.d(TAG, "onPostTask: started");
|
||||||
|
if (currentPage == 1) {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
mRecyclerView.setVisibility(View.VISIBLE);
|
||||||
|
cancelFAB.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows a toast when we got an exception and returns back to main activity
|
||||||
|
@Override
|
||||||
|
public void taskFailed(Exception e, int statusCode) {
|
||||||
|
Toast.makeText(SearchActivity.this,
|
||||||
|
getString(R.string.connection_failed),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
// TODO: 24.11.2019 log Exception e and int statusCode
|
||||||
|
Log.e(TAG, "onFailure: " + e.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
floatingActionButton.setOnClickListener(view -> {
|
||||||
|
mRecyclerView.smoothScrollToPosition(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
viewModel.addListener(listener);
|
||||||
|
// Send a request to parse a search results from an URL and get results back
|
||||||
|
// to activity via callback method getSearchResults()
|
||||||
|
viewModel.newSearchRequest(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void newPageLoader(int maxPage, ArrayList<Lyric> items) {
|
||||||
|
if (currentPage != PAGE_START) {
|
||||||
|
mRecyclerView.post(() -> adapter.removeLoading());
|
||||||
|
}
|
||||||
|
mRecyclerView.post(() -> adapter.addItems(items));
|
||||||
|
// check whether is last page or not
|
||||||
|
if (currentPage < maxPage) {
|
||||||
|
mRecyclerView.post(() -> adapter.addLoading());
|
||||||
|
} else {
|
||||||
|
isLastPage = true;
|
||||||
|
}
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp(){
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
Log.d(TAG, "onDestroy: activated");
|
||||||
|
adapter.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_main, menu);
|
||||||
|
// Get saved data and set value to the checkable menu item
|
||||||
|
MenuItem darkThemeItem = menu.findItem(R.id.dark_theme);
|
||||||
|
PreferencesManager.initializeInstance(this);
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
darkThemeItem.setChecked(pm.getDarkThemeSelected());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.toolbar_show_favs:
|
||||||
|
// Open favorites activity
|
||||||
|
Intent intent = new Intent(this, FavoritesActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
case R.id.quit:
|
||||||
|
this.finishAffinity();
|
||||||
|
return true;
|
||||||
|
case R.id.dark_theme:
|
||||||
|
// Switch color theme of the app (light/dark)
|
||||||
|
item.setChecked(!item.isChecked());
|
||||||
|
// Save to value
|
||||||
|
PreferencesManager.initializeInstance(this);
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
pm.setDarkThemeSelected(item.isChecked());
|
||||||
|
if (item.isChecked()) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
|
} else {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void intentLoader() {
|
||||||
|
Intent intent = getIntent();
|
||||||
|
searchInput = intent.getStringExtra(LauncherActivity.SEARCH_QUERY_SONG);
|
||||||
|
spinnerPos = intent.getIntExtra(LauncherActivity.SPINNER_LIST, 0);
|
||||||
|
artistText = intent.getStringExtra(LauncherActivity.ARTIST_INPUT);
|
||||||
|
songText = intent.getStringExtra(LauncherActivity.SONG_INPUT);
|
||||||
|
lyricsText = intent.getStringExtra(LauncherActivity.LYRICS_INPUT);
|
||||||
|
artistSpinnerPos = intent.getIntExtra(LauncherActivity.ARTIST_SPINNER, 0);
|
||||||
|
songSpinnerPos = intent.getIntExtra(LauncherActivity.SONG_SPINNER, 0);
|
||||||
|
lyricsSpinnerPos = intent.getIntExtra(LauncherActivity.LYRICS_SPINNER, 0);
|
||||||
|
ignoreMainSearch = intent.getBooleanExtra(LauncherActivity.IGNORE_MAIN_SEARCH_INPUT, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoadCancel(View view) {
|
||||||
|
if (task != null) {
|
||||||
|
task.cancel(true);
|
||||||
|
task = null;
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
383
app/src/main/java/com/bernd32/jlyrics/ui/ShowLyricsActivity.java
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.ui;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.style.StyleSpan;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.appcompat.widget.ShareActionProvider;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.core.view.MenuItemCompat;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import com.bernd32.jlyrics.R;
|
||||||
|
import com.bernd32.jlyrics.async.AsyncTaskListener;
|
||||||
|
import com.bernd32.jlyrics.async.GetDataAsyncTask;
|
||||||
|
import com.bernd32.jlyrics.async.RomanizeAsyncTask;
|
||||||
|
import com.bernd32.jlyrics.database.FavLyrics;
|
||||||
|
import com.bernd32.jlyrics.database.LyricsViewModel;
|
||||||
|
import com.bernd32.jlyrics.utils.PreferencesManager;
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
|
||||||
|
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import static com.bernd32.jlyrics.utils.HelperClass.addNewLines;
|
||||||
|
|
||||||
|
public class ShowLyricsActivity extends AppCompatActivity implements ChangeFontDialogFragment.OnDialogButtonClick {
|
||||||
|
|
||||||
|
private static final String TAG = "ShowLyricsActivity";
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
public TextView lyricsTV;
|
||||||
|
private String lyrics = "";
|
||||||
|
private String romanizedLyrics = "";
|
||||||
|
private String title;
|
||||||
|
private String songUrl;
|
||||||
|
private String cardDesc;
|
||||||
|
private String cardTitle;
|
||||||
|
private String imgUrl;
|
||||||
|
private LyricsViewModel lyricsViewModel;
|
||||||
|
private boolean hasLyrics;
|
||||||
|
private ExtendedFloatingActionButton cancelFAB;
|
||||||
|
private GetDataAsyncTask task;
|
||||||
|
private boolean romanized = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreate: activated");
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
PreferencesManager.initializeInstance(this);
|
||||||
|
setContentView(R.layout.activity_show_lyrics);
|
||||||
|
lyricsTV = findViewById(R.id.lyrics);
|
||||||
|
progressBar = findViewById(R.id.progress_bar);
|
||||||
|
cancelFAB = findViewById(R.id.load_cancel);
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
assert actionBar != null;
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
// For saving lyrics to favs
|
||||||
|
lyricsViewModel = new ViewModelProvider(this).get(LyricsViewModel.class);
|
||||||
|
loadIntents();
|
||||||
|
try {
|
||||||
|
isLyricsInDB(songUrl);
|
||||||
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// Load lyrics from savedInstanceState, so we don't have to call startAsyncTask
|
||||||
|
// if activity is stopped or destroyed
|
||||||
|
loadLyrics(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadLyrics(Bundle savedInstanceState) {
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
startAsyncTask(songUrl);
|
||||||
|
} else {
|
||||||
|
romanized = savedInstanceState.getBoolean("romanized");
|
||||||
|
lyrics = savedInstanceState.getString("saved_lyrics");
|
||||||
|
romanizedLyrics = savedInstanceState.getString("saved_romaji_lyrics");
|
||||||
|
if (!romanized) {
|
||||||
|
lyricsTV.setText(lyrics);
|
||||||
|
} else {
|
||||||
|
lyricsTV.setText(romanizedLyrics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
Log.d(TAG, "onStop: started");
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
Log.d(TAG, "onSaveInstanceState: start");
|
||||||
|
outState.putString("saved_lyrics", lyrics);
|
||||||
|
outState.putString("saved_romaji_lyrics", romanizedLyrics);
|
||||||
|
outState.putBoolean("romanized", romanized);
|
||||||
|
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
getMenuInflater().inflate(R.menu.menu_lyrics, menu);
|
||||||
|
MenuItem menuShareItem = menu.findItem(R.id.toolbar_share);
|
||||||
|
ShareActionProvider shareActionProvider = (ShareActionProvider)
|
||||||
|
MenuItemCompat.getActionProvider(menuShareItem);
|
||||||
|
// Get saved data and set values
|
||||||
|
MenuItem darkThemeItem = menu.findItem(R.id.dark_theme);
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
darkThemeItem.setChecked(pm.getDarkThemeSelected());
|
||||||
|
int fontSize = pm.getFontSize();
|
||||||
|
lyricsTV.setTextSize((float) fontSize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.romanize:
|
||||||
|
convertLyrics();
|
||||||
|
return true;
|
||||||
|
case R.id.toolbar_share:
|
||||||
|
Intent sendIntent = new Intent();
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, lyrics);
|
||||||
|
sendIntent.setType("text/plain");
|
||||||
|
Intent shareIntent = Intent.createChooser(sendIntent, title);
|
||||||
|
startActivity(shareIntent);
|
||||||
|
return true;
|
||||||
|
case R.id.toolbar_furigana:
|
||||||
|
openFuriganaActivity();
|
||||||
|
return true;
|
||||||
|
case R.id.fav_add_remove:
|
||||||
|
saveLyrics();
|
||||||
|
return true;
|
||||||
|
case R.id.dark_theme:
|
||||||
|
item.setChecked(!item.isChecked());
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
pm.setDarkThemeSelected(item.isChecked());
|
||||||
|
if (item.isChecked()) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
|
} else {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case R.id.font_size:
|
||||||
|
showFontSizeDialog();
|
||||||
|
return true;
|
||||||
|
case R.id.quit:
|
||||||
|
this.finishAffinity();
|
||||||
|
return true;
|
||||||
|
case R.id.copy:
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip = ClipData.newPlainText(getString(R.string.lyrics), lyrics);
|
||||||
|
assert clipboard != null;
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toast.makeText(this, getString(R.string.copied), Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertLyrics() {
|
||||||
|
if (!romanized) {
|
||||||
|
RomanizeAsyncTask romanizeTask = new RomanizeAsyncTask(new AsyncTaskListener<String>() {
|
||||||
|
@Override
|
||||||
|
public void onPreTask() {
|
||||||
|
lyricsTV.setText("");
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
cancelFAB.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostTask(String string) {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
cancelFAB.setVisibility(View.GONE);
|
||||||
|
ShowLyricsActivity.this.romanizedLyrics = string;
|
||||||
|
lyricsTV.setText(string);
|
||||||
|
romanized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e, int statusCode) {
|
||||||
|
Toast.makeText(ShowLyricsActivity.this,
|
||||||
|
getString(R.string.connection_failed),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
// TODO: 24.11.2019 log Exception e and int statusCode
|
||||||
|
Log.d(TAG, "onFailure: " + e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
romanizeTask.execute(lyrics);
|
||||||
|
} else {
|
||||||
|
// If the text is already converted to romaji then show original Japanese text
|
||||||
|
lyricsTV.setText(lyrics);
|
||||||
|
romanized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openFuriganaActivity() {
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
// Create an intent and open a new activity
|
||||||
|
Intent intent = new Intent(this, FuriganaActivity.class);
|
||||||
|
intent.putExtra("lyrics_text", lyrics);
|
||||||
|
intent.putExtra("font_size", lyricsTV.getTextSize());
|
||||||
|
intent.putExtra("theme", pm.getDarkThemeSelected());
|
||||||
|
if (intent.resolveActivity(getPackageManager()) != null) {
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOkClicked(DialogFragment dialog) {
|
||||||
|
Toast.makeText(this, getString(R.string.saved), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelClicked(DialogFragment dialog) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeFontSize(int i) {
|
||||||
|
// Get data from DialogFragment interface,
|
||||||
|
// change font size of lyric text and save the value
|
||||||
|
lyricsTV.setTextSize((float) i);
|
||||||
|
PreferencesManager pm = PreferencesManager.getInstance();
|
||||||
|
pm.setFontSize(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp(){
|
||||||
|
if (task != null)
|
||||||
|
task.cancel(true);
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
|
if (hasLyrics)
|
||||||
|
menu.findItem(R.id.fav_add_remove).setTitle(getString(R.string.favs_remove));
|
||||||
|
else
|
||||||
|
menu.findItem(R.id.fav_add_remove).setTitle(getString(R.string.favs_add));
|
||||||
|
|
||||||
|
if (romanized) {
|
||||||
|
menu.findItem(R.id.romanize).setTitle(getString(R.string.show_original));
|
||||||
|
} else {
|
||||||
|
menu.findItem(R.id.romanize).setTitle(getString(R.string.romanize));
|
||||||
|
}
|
||||||
|
return super.onPrepareOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoadCancel(View view) {
|
||||||
|
if (task != null)
|
||||||
|
task.cancel(true);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFontSizeDialog() {
|
||||||
|
DialogFragment changeFontAlert = new ChangeFontDialogFragment();
|
||||||
|
changeFontAlert.show(getSupportFragmentManager(), "changeFontAlert");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadIntents() {
|
||||||
|
if (getIntent().hasExtra("song_url")){
|
||||||
|
Log.d(TAG, "getIncomingIntent: found intent extras.");
|
||||||
|
songUrl = getIntent().getStringExtra("song_url");
|
||||||
|
}
|
||||||
|
imgUrl = getIntent().getStringExtra("img_url");
|
||||||
|
cardTitle = getIntent().getStringExtra("title");
|
||||||
|
cardDesc = getIntent().getStringExtra("description");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void isLyricsInDB(String songUrl) throws ExecutionException, InterruptedException {
|
||||||
|
this.hasLyrics = lyricsViewModel.isLyricsExists(songUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveLyrics() {
|
||||||
|
if (!hasLyrics) {
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
FavLyrics savedLyrics = new FavLyrics(cardTitle, cardDesc, songUrl, imgUrl, timestamp);
|
||||||
|
lyricsViewModel.insert(savedLyrics);
|
||||||
|
Toast.makeText(this, getString(R.string.saved), Toast.LENGTH_SHORT).show();
|
||||||
|
this.hasLyrics = true;
|
||||||
|
} else {
|
||||||
|
// Remove lyrics
|
||||||
|
lyricsViewModel.deleteLyricsByUrl(songUrl);
|
||||||
|
Toast.makeText(this, getString(R.string.removed), Toast.LENGTH_SHORT).show();
|
||||||
|
this.hasLyrics = false;
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startAsyncTask(String url) {
|
||||||
|
Log.d(TAG, "startAsyncTask: " + url);
|
||||||
|
task = new GetDataAsyncTask(new AsyncTaskListener<Document>() {
|
||||||
|
@Override
|
||||||
|
public void onPreTask() {
|
||||||
|
Log.d(TAG, "onPreTask: started");
|
||||||
|
// show progressbar only in initial loading
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
cancelFAB.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostTask(Document doc) {
|
||||||
|
Log.d(TAG, "onPostTask: started");
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
cancelFAB.setVisibility(View.GONE);
|
||||||
|
showLyrics(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e, int statusCode) {
|
||||||
|
Toast.makeText(ShowLyricsActivity.this,
|
||||||
|
getString(R.string.connection_failed),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
// TODO: 24.11.2019 log Exception e and int statusCode
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.execute(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLyrics(Document doc) {
|
||||||
|
Log.d(TAG, "showLyrics: activated");
|
||||||
|
lyrics = doc.select("#Lyric").first().html();
|
||||||
|
String songName = doc.select("#mnb > div.cap > h2")
|
||||||
|
.text()
|
||||||
|
.replace(" 歌詞", "");
|
||||||
|
title = String.format(Locale.getDefault(), "「%s」\n\n\n", songName);
|
||||||
|
ShowLyricsActivity.this.setTitle(title);
|
||||||
|
lyrics = addNewLines(lyrics);
|
||||||
|
lyrics = lyrics.replace("(△くり返し)", getText(R.string.repeat));
|
||||||
|
lyrics = lyrics.replace("(※くり返し)", getText(R.string.repeat2));
|
||||||
|
SpannableString songTitle = new SpannableString(title);
|
||||||
|
songTitle.setSpan(new StyleSpan(Typeface.BOLD), 0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
lyricsTV.setText(songTitle);
|
||||||
|
lyricsTV.append(lyrics);
|
||||||
|
Log.d(TAG, "Current artist: " + cardDesc);
|
||||||
|
Log.d(TAG, "Current song: " + cardTitle);
|
||||||
|
}
|
||||||
|
}
|
87
app/src/main/java/com/bernd32/jlyrics/utils/HelperClass.java
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.utils;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.safety.Whitelist;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public class HelperClass {
|
||||||
|
static final List<String> userAgents = Arrays.asList("Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16D57",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15G77",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
|
||||||
|
|
||||||
|
public static String getUserAgent() {
|
||||||
|
return userAgents.get(ThreadLocalRandom.current().nextInt((userAgents.size())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String urlBuilder(int page, int spinnerPos, String searchInput) {
|
||||||
|
// Used for simple search
|
||||||
|
StringBuilder url = new StringBuilder("http://search.j-lyric.net/index.php?&ct=2&ca=2&cl=2&p=");
|
||||||
|
final int SONG_NAME_SELECTED = 0;
|
||||||
|
final int ARTIST_SELECTED = 1;
|
||||||
|
final int LYRICS_SELECTED = 2;
|
||||||
|
switch (spinnerPos) {
|
||||||
|
case SONG_NAME_SELECTED:
|
||||||
|
url.append(page).append("&kt=").append(searchInput);
|
||||||
|
break;
|
||||||
|
case ARTIST_SELECTED:
|
||||||
|
url.append(page).append("&ka=").append(searchInput);
|
||||||
|
break;
|
||||||
|
case LYRICS_SELECTED:
|
||||||
|
url.append(page).append("&kl=").append(searchInput);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String urlBuilder(int page, String artistText, int artistSpinnerPos, String songText,
|
||||||
|
int songSpinnerPos, String lyricsText, int lyricsSpinnerPos) {
|
||||||
|
// Used for detailed search
|
||||||
|
if (lyricsSpinnerPos > 0) lyricsSpinnerPos += 1;
|
||||||
|
String url = String.format(Locale.getDefault(),
|
||||||
|
"http://search.j-lyric.net/index.php?p=%d&kt=%s&ct=%d&ka=%s&ca=%d&kl=%s&cl=%d",
|
||||||
|
page, songText, songSpinnerPos, artistText, artistSpinnerPos, lyricsText, lyricsSpinnerPos);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String addNewLines(String html) {
|
||||||
|
// Add new lines to the fetched text
|
||||||
|
Document document = Jsoup.parse(html);
|
||||||
|
// Makes html() preserve linebreaks and spacing
|
||||||
|
document.outputSettings(new Document.OutputSettings().prettyPrint(false));
|
||||||
|
document.select("br").append("\\n");
|
||||||
|
document.select("p").prepend("\\n\\n");
|
||||||
|
String s = document.html().replaceAll("\\\\n", "\n");
|
||||||
|
return Jsoup.clean(s, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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.jlyrics.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
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 FONT_SIZE = "com.example.app.fontsize";
|
||||||
|
private static final String DARK_THEME = "com.example.app.darktheme";
|
||||||
|
|
||||||
|
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 setFontSize(int value) {
|
||||||
|
mPref.edit()
|
||||||
|
.putInt(FONT_SIZE, value)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFontSize() {
|
||||||
|
return mPref.getInt(FONT_SIZE, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDarkThemeSelected(boolean value) {
|
||||||
|
mPref.edit()
|
||||||
|
.putBoolean(DARK_THEME, value)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDarkThemeSelected() {
|
||||||
|
return mPref.getBoolean(DARK_THEME, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(String key) {
|
||||||
|
mPref.edit()
|
||||||
|
.remove(key)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean clear() {
|
||||||
|
return mPref.edit()
|
||||||
|
.clear()
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
}
|
BIN
app/src/main/res/drawable-hdpi/baseline_g_translate_white_24.png
Normal file
After Width: | Height: | Size: 536 B |
BIN
app/src/main/res/drawable-hdpi/baseline_search_white_24.png
Normal file
After Width: | Height: | Size: 373 B |
BIN
app/src/main/res/drawable-hdpi/baseline_share_white_24.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
app/src/main/res/drawable-mdpi/baseline_g_translate_white_24.png
Normal file
After Width: | Height: | Size: 348 B |
BIN
app/src/main/res/drawable-mdpi/baseline_search_white_24.png
Normal file
After Width: | Height: | Size: 244 B |
BIN
app/src/main/res/drawable-mdpi/baseline_share_white_24.png
Normal file
After Width: | Height: | Size: 249 B |
After Width: | Height: | Size: 635 B |
BIN
app/src/main/res/drawable-xhdpi/baseline_search_white_24.png
Normal file
After Width: | Height: | Size: 411 B |
BIN
app/src/main/res/drawable-xhdpi/baseline_share_white_24.png
Normal file
After Width: | Height: | Size: 452 B |
After Width: | Height: | Size: 938 B |
BIN
app/src/main/res/drawable-xxhdpi/baseline_search_white_24.png
Normal file
After Width: | Height: | Size: 624 B |
BIN
app/src/main/res/drawable-xxhdpi/baseline_share_white_24.png
Normal file
After Width: | Height: | Size: 654 B |
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/baseline_search_white_24.png
Normal file
After Width: | Height: | Size: 822 B |
BIN
app/src/main/res/drawable-xxxhdpi/baseline_share_white_24.png
Normal file
After Width: | Height: | Size: 848 B |
25
app/src/main/res/drawable/favorite_border_black.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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="#FFFFFF"
|
||||||
|
android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
|
||||||
|
</vector>
|
25
app/src/main/res/drawable/ic_cancel_black_24dp.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
|
||||||
|
</vector>
|
25
app/src/main/res/drawable/ic_filter_list_24dp.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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="M10,18h4v-2h-4v2zM3,6v2h18L21,6L3,6zM6,13h12v-2L6,11v2z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,21 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
|
||||||
|
</vector>
|
182
app/src/main/res/layout-land/activity_main.xml
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:fillViewport="false">
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
tools:context=".ui.LauncherActivity">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:onClick="onLoadLyrics"
|
||||||
|
android:text="@android:string/search_go"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/spinner"
|
||||||
|
android:drawableStart="@drawable/baseline_search_white_24"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/search_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:layout_constraintHorizontal_weight="3"
|
||||||
|
android:hint="@string/search"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/spinner"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinner"
|
||||||
|
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/button"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/search_input"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/detailed_search_button"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:text="@string/detailed_search_show"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/button"
|
||||||
|
android:onClick="onDetailedSearch" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/artist_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="0dp"
|
||||||
|
android:hint="@string/artist"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/artist_spinner"
|
||||||
|
app:layout_constraintHorizontal_weight="3"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/artist_spinner"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detailed_search_button"
|
||||||
|
android:importantForAutofill="no" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/artist_spinner"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/artist_input"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detailed_search_button" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/song_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="0dp"
|
||||||
|
android:hint="@string/song"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/song_spinner"
|
||||||
|
app:layout_constraintHorizontal_weight="3"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/song_spinner"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/artist_input" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/song_spinner"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/song_input"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/artist_spinner" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/lyrics_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="0dp"
|
||||||
|
android:hint="@string/lyrics"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/lyrics_spinner"
|
||||||
|
app:layout_constraintHorizontal_weight="3"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/lyrics_spinner"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/song_input" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/lyrics_spinner"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/lyrics_input"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/song_spinner" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="lyrics_input,song_spinner,song_input,artist_spinner,artist_input,lyrics_spinner" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
57
app/src/main/res/layout/activity_favorites.xml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.FavoritesActivity">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view_favs"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_message"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:text="@string/empty_message"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/toolbar"
|
||||||
|
android:visibility="invisible"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
78
app/src/main/res/layout/activity_furigana.xml
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.FuriganaActivity">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar_main"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_bar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="313dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/linearLayout"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/load_cancel"
|
||||||
|
layout="@layout/cancel_fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:layout_anchorGravity="bottom"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/linearLayout"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1.0"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbar">
|
||||||
|
|
||||||
|
<WebView
|
||||||
|
android:id="@+id/webview"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="10dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
227
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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:paddingBottom="24dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/artist_input"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/artist_input"
|
||||||
|
tools:context=".ui.LauncherActivity">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:icon="@drawable/baseline_search_white_24"
|
||||||
|
android:text="@string/search"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:onClick="onLoadLyrics"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/detailed_search_button"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="1"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/spinner" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/search_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:hint="@string/search"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinner"
|
||||||
|
style="@style/Base.Widget.AppCompat.Spinner.Underlined"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="4"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/search_input" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/detailed_search_button"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:icon="@drawable/ic_filter_list_24dp"
|
||||||
|
android:onClick="onDetailedSearch"
|
||||||
|
android:text="@string/detailed_search_show"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
app:borderWidth="0dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/button" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/artist_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:hint="@string/artist"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="3"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/artist_spinner"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detailed_search_button" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/artist_spinner"
|
||||||
|
style="@style/Base.Widget.AppCompat.Spinner.Underlined"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/song_input"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/artist_input" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/song_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:hint="@string/song"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.98"
|
||||||
|
app:layout_constraintHorizontal_weight="3"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/song_spinner"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/artist_spinner" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/song_spinner"
|
||||||
|
style="@style/Base.Widget.AppCompat.Spinner.Underlined"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/lyrics_input"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/song_input" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/lyrics_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:hint="@string/lyrics"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="1.0"
|
||||||
|
app:layout_constraintHorizontal_weight="3"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/lyrics_spinner"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/song_spinner" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/lyrics_spinner"
|
||||||
|
style="@style/Base.Widget.AppCompat.Spinner.Underlined"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/lyrics_input" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button2"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:icon="@drawable/baseline_search_white_24"
|
||||||
|
android:text="@string/search"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:onClick="onLoadLyrics"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="1"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/lyrics_spinner" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
|
app:constraint_referenced_ids="lyrics_input,song_spinner,song_input,artist_spinner,artist_input,lyrics_spinner,button2" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
83
app/src/main/res/layout/activity_search.xml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.SearchActivity">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerv_view"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="?attr/actionBarSize"
|
||||||
|
app:layout_behavior = "@string/appbar_scrolling_view_behavior"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_bar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:visibility="invisible"
|
||||||
|
tools:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/floating_action_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="30dp"
|
||||||
|
android:layout_marginEnd="248dp"
|
||||||
|
app:fabSize="normal"
|
||||||
|
android:src="@drawable/ic_keyboard_arrow_up_white_24dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
tools:visibility="visible"
|
||||||
|
app:backgroundTint="@color/colorAccent"
|
||||||
|
app:tint="?android:attr/textColorPrimaryInverse"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:layout_anchorGravity="bottom"
|
||||||
|
layout="@layout/cancel_fab"
|
||||||
|
android:id="@+id/load_cancel"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/recyclerv_view"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
78
app/src/main/res/layout/activity_show_lyrics.xml
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:context=".ui.ShowLyricsActivity">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/lyrics"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:lineSpacingMultiplier="1.3"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_bar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/load_cancel"
|
||||||
|
layout="@layout/cancel_fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:layout_anchorGravity="bottom"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
34
app/src/main/res/layout/cancel_fab.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:backgroundTint="@color/colorAccent"
|
||||||
|
android:text="@string/cancel"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="?android:attr/textColorPrimaryInverse"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:icon="@drawable/ic_cancel_black_24dp"
|
||||||
|
app:iconTint="?android:attr/textColorPrimaryInverse"
|
||||||
|
tools:visibility="visible"
|
||||||
|
android:onClick="onLoadCancel"
|
||||||
|
android:scaleX="0.7"
|
||||||
|
android:scaleY="0.7"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" />
|
40
app/src/main/res/layout/dialog_change_fontsize.xml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
<SeekBar
|
||||||
|
android:max="100"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/seek_bar"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_marginTop="74dp">
|
||||||
|
</SeekBar>
|
||||||
|
<TextView
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/font_size_textview"
|
||||||
|
android:paddingTop="15dp"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:layout_width="wrap_content" android:layout_alignParentTop="true"
|
||||||
|
android:layout_centerHorizontal="true">
|
||||||
|
</TextView>
|
||||||
|
</RelativeLayout>
|
39
app/src/main/res/layout/item_loading.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
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">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
style="?android:attr/progressBarStyle" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
82
app/src/main/res/layout/layout_listitem.xml
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/Widget.MaterialComponents.CardView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:id="@+id/parent_layout"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/card_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="Song name"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/image"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/card_description"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="Artist"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/image"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/card_title"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image"
|
||||||
|
android:layout_width="75dp"
|
||||||
|
android:layout_height="75dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@android:color/darker_gray"
|
||||||
|
android:contentDescription="@string/album_photo" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
23
app/src/main/res/layout/toolbar_main.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
|
||||||
|
app:popupTheme="@style/ThemeOverlay.MaterialComponents.Light" />
|
23
app/src/main/res/menu/menu_favorites.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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>
|
61
app/src/main/res/menu/menu_lyrics.xml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item android:id="@+id/fav_add_remove"
|
||||||
|
android:title="@string/add_favorites"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/romanize"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/romanize" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/toolbar_furigana"
|
||||||
|
android:icon="@drawable/baseline_g_translate_white_24"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/translate">
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:id="@+id/toolbar_share"
|
||||||
|
android:icon="@drawable/baseline_share_white_24"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/share">
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:id="@+id/copy"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/copy">
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:id="@+id/font_size"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/font_size">
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:id="@+id/dark_theme"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/dark_theme"
|
||||||
|
android:checkable="true">
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:id="@+id/quit"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/quit">
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</menu>
|
40
app/src/main/res/menu/menu_main.xml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item android:id="@+id/toolbar_show_favs"
|
||||||
|
android:title="@string/favs"
|
||||||
|
android:icon="@drawable/favorite_border_black"
|
||||||
|
app:showAsAction="ifRoom|withText" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/dark_theme"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/dark_theme"
|
||||||
|
android:checkable="true">
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:id="@+id/contact"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/contact">
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:id="@+id/quit"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/quit">
|
||||||
|
</item>
|
||||||
|
</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.8 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 11 KiB |
22
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#295d8a</color>
|
||||||
|
<color name="colorPrimaryDark">#1d4161</color>
|
||||||
|
<color name="colorAccent">#398bd4</color>
|
||||||
|
</resources>
|
20
app/src/main/res/values/ic_launcher_background.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#F1F1F1</color>
|
||||||
|
</resources>
|
79
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">JLyrics</string>
|
||||||
|
<string name="search">Search</string>
|
||||||
|
<string name="share">Share</string>
|
||||||
|
<string name="title_found">Found:</string>
|
||||||
|
<string name="album_photo">Album photo</string>
|
||||||
|
<string name="detailed_search_hide">Hide detailed search</string>
|
||||||
|
<string name="detailed_search_show">Show detailed search</string>
|
||||||
|
<string name="artist">Artist</string>
|
||||||
|
<string name="song">Song</string>
|
||||||
|
<string name="lyrics">Lyrics</string>
|
||||||
|
<string name="ok">OK</string>
|
||||||
|
<string name="not_found_title">No result found</string>
|
||||||
|
<string name="repeat"><i>(△Repeat)</i></string>
|
||||||
|
<string name="repeat2"><i>( ※ Repeat)</i></string>
|
||||||
|
<string name="not_found_message">Suggestions:\n\n
|
||||||
|
• Make sure all words are spelled correctly\n
|
||||||
|
• Try searching in Japanese\n
|
||||||
|
• Try different search options\n
|
||||||
|
• Try using detailed search
|
||||||
|
</string>
|
||||||
|
<string name="translate">Show furigana</string>
|
||||||
|
<string name="add_favorites">Add/Remove to favorites</string>
|
||||||
|
<string name="delete_all">Delete all</string>
|
||||||
|
<string name="delete_lyrics_preamble">Removed from favorites</string>
|
||||||
|
<string name="favs_remove">Remove from favs</string>
|
||||||
|
<string name="favs_add">Add to favs</string>
|
||||||
|
<string name="favs">Favorites</string>
|
||||||
|
<string name="copy">Copy</string>
|
||||||
|
<string name="font_size">Change font size</string>
|
||||||
|
<string name="dark_theme">Night mode</string>
|
||||||
|
<string name="quit">Quit</string>
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
|
<string name="change_font">Change font size</string>
|
||||||
|
<string name="saved">Saved</string>
|
||||||
|
<string name="removed">Removed</string>
|
||||||
|
<string name="contact">Contact developer</string>
|
||||||
|
<string name="copied">Copied</string>
|
||||||
|
<string name="connection_failed">Connection failed</string>
|
||||||
|
<string name="romanize">Romanize</string>
|
||||||
|
<string name="show_original">Show original</string>
|
||||||
|
<string name="email_address">fallentides@outlook.com</string>
|
||||||
|
<string name="email_subject_question">Feedback</string>
|
||||||
|
<string name="empty_message">Favorites are empty</string>
|
||||||
|
|
||||||
|
<string-array name="spinner_items">
|
||||||
|
<item>Song name</item>
|
||||||
|
<item>Artist</item>
|
||||||
|
<item>Lyrics</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="spinner_search_options">
|
||||||
|
<item>Starts with</item>
|
||||||
|
<item>Full match</item>
|
||||||
|
<item>In the middle</item>
|
||||||
|
<item>Ends with</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="lyrics_spinner_options">
|
||||||
|
<item>Starts with</item>
|
||||||
|
<item>In the middle</item>
|
||||||
|
<item>Ends with</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
</resources>
|
27
app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
25
app/src/main/res/xml/network_security_config.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">j-lyric.net</domain>
|
||||||
|
</domain-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">images-amazon.com</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
192
app/src/main/resources/kana_to_romaji.csv
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
ア,a
|
||||||
|
イ,i
|
||||||
|
ウ,u
|
||||||
|
エ,e
|
||||||
|
オ,o
|
||||||
|
ー,-
|
||||||
|
ァ,xa
|
||||||
|
ィ,xi
|
||||||
|
ゥ,xu
|
||||||
|
ェ,xe
|
||||||
|
ォ,xo
|
||||||
|
カ,ka
|
||||||
|
キ,ki
|
||||||
|
ク,ku
|
||||||
|
ケ,ke
|
||||||
|
コ,ko
|
||||||
|
ガ,ga
|
||||||
|
ギ,gi
|
||||||
|
グ,gu
|
||||||
|
ゲ,ge
|
||||||
|
ゴ,go
|
||||||
|
サ,sa
|
||||||
|
シ,shi
|
||||||
|
ス,su
|
||||||
|
セ,se
|
||||||
|
ソ,so
|
||||||
|
ザ,za
|
||||||
|
ジ,ji
|
||||||
|
ズ,zu
|
||||||
|
ゼ,ze
|
||||||
|
ゾ,zo
|
||||||
|
ジャ,ja
|
||||||
|
ジュ,ju
|
||||||
|
ジェ,je
|
||||||
|
ジョ,jo
|
||||||
|
タ,ta
|
||||||
|
チ,chi
|
||||||
|
ツ,tsu
|
||||||
|
テ,te
|
||||||
|
ト,to
|
||||||
|
ダ,da
|
||||||
|
ヂ,dji
|
||||||
|
ヅ,dzu
|
||||||
|
デ,de
|
||||||
|
ド,do
|
||||||
|
ナ,na
|
||||||
|
ニ,ni
|
||||||
|
ヌ,nu
|
||||||
|
ネ,ne
|
||||||
|
ノ,no
|
||||||
|
ハ,ha
|
||||||
|
ヒ,hi
|
||||||
|
フ,fu
|
||||||
|
ヘ,he
|
||||||
|
ホ,ho
|
||||||
|
バ,ba
|
||||||
|
ビ,bi
|
||||||
|
ブ,bu
|
||||||
|
ベ,be
|
||||||
|
ボ,bo
|
||||||
|
パ,pa
|
||||||
|
ピ,pi
|
||||||
|
プ,pu
|
||||||
|
ペ,pe
|
||||||
|
ポ,po
|
||||||
|
ヴァ,va
|
||||||
|
ヴィ,vi
|
||||||
|
ヴ,vu
|
||||||
|
ヴェ,ve
|
||||||
|
ヴォ,vo
|
||||||
|
ファ,fa
|
||||||
|
フィ,fi
|
||||||
|
フェ,fe
|
||||||
|
フォ,fo
|
||||||
|
マ,ma
|
||||||
|
ミ,mi
|
||||||
|
ム,mu
|
||||||
|
メ,me
|
||||||
|
モ,mo
|
||||||
|
ヤ,ya
|
||||||
|
ユ,yu
|
||||||
|
イェ,ye
|
||||||
|
ヨ,yo
|
||||||
|
ラ,ra
|
||||||
|
リ,ri
|
||||||
|
ル,ru
|
||||||
|
レ,re
|
||||||
|
ロ,ro
|
||||||
|
ワ,wa
|
||||||
|
ヰ,wi
|
||||||
|
ヱ,we
|
||||||
|
ヲ,wo
|
||||||
|
ン,n
|
||||||
|
ヵ,xka
|
||||||
|
ヶ,ga
|
||||||
|
ヮ,xwa
|
||||||
|
ャ,xya
|
||||||
|
ュ,xyu
|
||||||
|
ョ,xyo
|
||||||
|
キャ,kya
|
||||||
|
キィ,kyi
|
||||||
|
キュ,kyu
|
||||||
|
キェ,kye
|
||||||
|
キョ,kyo
|
||||||
|
ギャ,gya
|
||||||
|
ギィ,gyi
|
||||||
|
ギュ,gyu
|
||||||
|
ギェ,gye
|
||||||
|
ギョ,gyo
|
||||||
|
シャ,sha
|
||||||
|
シィ,syi
|
||||||
|
シュ,shu
|
||||||
|
シェ,she
|
||||||
|
ショ,sho
|
||||||
|
ジィ,jyi
|
||||||
|
チャ,cha
|
||||||
|
チィ,cyi
|
||||||
|
チュ,chu
|
||||||
|
チェ,che
|
||||||
|
チョ,cho
|
||||||
|
テャ,tha
|
||||||
|
ティ,thi
|
||||||
|
テュ,thu
|
||||||
|
テェ,the
|
||||||
|
テョ,tho
|
||||||
|
ヂャ,dya
|
||||||
|
ヂィ,dyi
|
||||||
|
ヂュ,dyu
|
||||||
|
ヂェ,dye
|
||||||
|
ヂョ,dyo
|
||||||
|
デャ,dha
|
||||||
|
ディ,dhi
|
||||||
|
デュ,dhu
|
||||||
|
デェ,dhe
|
||||||
|
デョ,dho
|
||||||
|
ニャ,nya
|
||||||
|
ニィ,nyi
|
||||||
|
ニュ,nyu
|
||||||
|
ニェ,nye
|
||||||
|
ニョ,nyo
|
||||||
|
ヒャ,hya
|
||||||
|
ヒィ,hyi
|
||||||
|
ヒュ,hyu
|
||||||
|
ヒェ,hye
|
||||||
|
ヒョ,hyo
|
||||||
|
ビャ,bya
|
||||||
|
ビィ,byi
|
||||||
|
ビュ,byu
|
||||||
|
ビェ,bye
|
||||||
|
ビョ,byo
|
||||||
|
ピャ,pya
|
||||||
|
ピィ,pyi
|
||||||
|
ピュ,pyu
|
||||||
|
ピェ,pye
|
||||||
|
ピョ,pyo
|
||||||
|
ミャ,mya
|
||||||
|
ミィ,myi
|
||||||
|
ミュ,myu
|
||||||
|
ミェ,mye
|
||||||
|
ミョ,myo
|
||||||
|
リャ,lya
|
||||||
|
リィ,lyi
|
||||||
|
リュ,lyu
|
||||||
|
リェ,lye
|
||||||
|
リョ,lyo
|
||||||
|
−,-
|
||||||
|
゛,"
|
||||||
|
゜,'
|
||||||
|
、,,
|
||||||
|
。,.
|
||||||
|
:,:
|
||||||
|
,
|
||||||
|
@,@
|
||||||
|
(,(
|
||||||
|
),)
|
||||||
|
,
|
||||||
|
ンア,n'a
|
||||||
|
んあ,n'a
|
||||||
|
ンイ,n'i
|
||||||
|
んい,n'i
|
||||||
|
ンウ,n'u
|
||||||
|
んう,n'u
|
||||||
|
ンエ,n'e
|
||||||
|
んえ,n'e
|
||||||
|
ンオ,n'o
|
||||||
|
んお,n'o
|
||||||
|
ンヤ,n'ya
|
||||||
|
んや,n'ya
|
||||||
|
ンユ,n'yu
|
||||||
|
んゆ,n'yu
|
||||||
|
ンヨ,n'yo
|
||||||
|
んよ,n'yo
|
Can't render this file because it contains an unexpected character in line 192 and column 13.
|
48
build.gradle
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
roomVersion = '2.2.2'
|
||||||
|
archLifecycleVersion = '2.2.0-rc02'
|
||||||
|
}
|