Initial commit retro music app
This commit is contained in:
parent
ab332473bc
commit
fe890632fd
932 changed files with 83126 additions and 0 deletions
9
.idea/RetroMusicPlayer.iml
Normal file
9
.idea/RetroMusicPlayer.iml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
29
.idea/codeStyles/Project.xml
Normal file
29
.idea/codeStyles/Project.xml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<Objective-C-extensions>
|
||||||
|
<file>
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||||
|
</file>
|
||||||
|
<class>
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||||
|
</class>
|
||||||
|
<extensions>
|
||||||
|
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||||
|
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||||
|
</extensions>
|
||||||
|
</Objective-C-extensions>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/RetroMusicPlayer.iml" filepath="$PROJECT_DIR$/.idea/RetroMusicPlayer.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
19
RetroMusicPlayer.iml
Normal file
19
RetroMusicPlayer.iml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.linked.project.id="RetroMusicPlayer" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="java-gradle" name="Java-Gradle">
|
||||||
|
<configuration>
|
||||||
|
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
|
||||||
|
<option name="BUILDABLE" value="false" />
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
226
app/app.iml
Normal file
226
app/app.iml
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="android-gradle" name="Android-Gradle">
|
||||||
|
<configuration>
|
||||||
|
<option name="GRADLE_PROJECT_PATH" value=":app" />
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
<facet type="android" name="Android">
|
||||||
|
<configuration>
|
||||||
|
<option name="SELECTED_BUILD_VARIANT" value="normalDebug" />
|
||||||
|
<option name="ASSEMBLE_TASK_NAME" value="assembleNormalDebug" />
|
||||||
|
<option name="COMPILE_JAVA_TASK_NAME" value="compileNormalDebugSources" />
|
||||||
|
<afterSyncTasks>
|
||||||
|
<task>generateNormalDebugSources</task>
|
||||||
|
</afterSyncTasks>
|
||||||
|
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||||
|
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||||
|
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||||
|
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||||
|
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||||
|
<output url="file://$MODULE_DIR$/build/intermediates/javac/normalDebug/compileNormalDebugJavaWithJavac/classes" />
|
||||||
|
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/normalDebugUnitTest/compileNormalDebugUnitTestJavaWithJavac/classes" />
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/normal/debug" isTestSource="false" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/normalDebug/processNormalDebugResources/r" isTestSource="false" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/normal/debug" isTestSource="false" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/normal/debug" isTestSource="false" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/normal/debug" isTestSource="false" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/normal/debug" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/normal/debug" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normalDebug/res" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normalDebug/resources" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normalDebug/assets" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normalDebug/aidl" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normalDebug/java" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normalDebug/rs" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normalDebug/shaders" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/normal/debug" isTestSource="true" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/normalDebugAndroidTest/processNormalDebugAndroidTestResources/r" isTestSource="true" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/normal/debug" isTestSource="true" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/normal/debug" isTestSource="true" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/normal/debug" isTestSource="true" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/normal/debug" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/normal/debug" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormalDebug/res" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormalDebug/resources" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormalDebug/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormalDebug/aidl" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormalDebug/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormalDebug/rs" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormalDebug/shaders" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/normal/debug" isTestSource="true" generated="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormalDebug/res" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormalDebug/resources" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormalDebug/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormalDebug/aidl" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormalDebug/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormalDebug/rs" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormalDebug/shaders" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normal/res" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normal/resources" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normal/assets" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normal/aidl" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normal/java" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normal/rs" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/normal/shaders" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormal/res" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormal/resources" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormal/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormal/aidl" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormal/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormal/rs" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNormal/shaders" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormal/res" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormal/resources" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormal/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormal/aidl" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormal/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormal/rs" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testNormal/shaders" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/aidl" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/build-info" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-apk" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_main_apk_resources" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split_list" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:support-v4-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:okhttp3-integration:1.5.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.sothree.slidinguppanel:library-3.4.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.7@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: __local_aars__:/Users/hemanths/Desktop/logo/RetroMusicPlayer/app/libs/jaudiotagger-android-2.2.3.jar:unspecified@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.afollestad:material-cab-0.1.12" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:palette-v7-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:recyclerview-v7-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.6@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:support-media-compat-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:multidex-1.0.3" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.3.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.h6ah4i.android.widget.advrecyclerview:advrecyclerview-0.11.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.github.kabouzeid:RecyclerView-FastScroll-1.0.16-kmod" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.14.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.github.ksoichiro:android-observablescrollview-1.6.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:support-fragment-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.3.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: __local_aars__:/Users/hemanths/Desktop/logo/RetroMusicPlayer/app/libs/jsoup-1.11.2.jar:unspecified@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: android.arch.core:runtime-1.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:design-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.afollestad.material-dialogs:core-0.9.6.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.github.jetradarmobile:android-snowfall-1.1.6" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.anjlab.android.iab.v3:library:1.0.44@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: uk.co.chrisjenx:calligraphy-2.3.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: __local_aars__:/Users/hemanths/Desktop/logo/RetroMusicPlayer/app/libs/juniversalchardet-1.0.3.jar:unspecified@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:gridlayout-v7-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:transition-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.jakewharton:butterknife-8.8.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:logging-interceptor:3.10.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:cardview-v7-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: io.reactivex.rxjava2:rxjava:2.1.9@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.r0adkll:slidableactivity-2.0.6" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.jakewharton:butterknife-annotations:8.8.1@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.reactivestreams:reactive-streams:1.0.2@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: me.zhanghai.android.materialprogressbar:library-1.4.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:glide:3.8.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.6@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.mpatric:mp3agic:0.8.3@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.nanohttpd:nanohttpd:2.3.1@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: jp.wasabeef:glide-transformations-2.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.afollestad.material-dialogs:commons-0.9.6.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: io.reactivex.rxjava2:rxandroid-2.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.10.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:preference-v7-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:adapter-rxjava2:2.3.0@jar" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.android.support:preference-v14-27.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" />
|
||||||
|
<orderEntry type="module" module-name="appthemehelper" />
|
||||||
|
</component>
|
||||||
|
</module>
|
145
app/build.gradle
Normal file
145
app/build.gradle
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 28
|
||||||
|
buildToolsVersion '28.0.1'
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 21
|
||||||
|
targetSdkVersion 28
|
||||||
|
|
||||||
|
renderscriptTargetApi 28 //must match target sdk and build tools
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
|
applicationId "code.name.monkey.retromusic"
|
||||||
|
versionCode 203
|
||||||
|
versionName 'R - 1.6.400'
|
||||||
|
|
||||||
|
multiDexEnabled true
|
||||||
|
|
||||||
|
buildConfigField("String", "LASTFM_API_KEY", "\"${getProperty(getProperties('../public.properties'), 'LASTFM_API_KEY')}\"")
|
||||||
|
buildConfigField("String", "GOOGLE_PLAY_LICENSE_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled true
|
||||||
|
shrinkResources true
|
||||||
|
zipAlignEnabled true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
|
||||||
|
resValue "string", "cast_app_id", "D3C555E1"
|
||||||
|
}
|
||||||
|
debug {
|
||||||
|
applicationIdSuffix '.debug'
|
||||||
|
versionNameSuffix ' DEBUG'
|
||||||
|
|
||||||
|
resValue "string", "cast_app_id", "D3C555E1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flavorDimensions "default"
|
||||||
|
productFlavors {
|
||||||
|
normal {
|
||||||
|
versionCode defaultConfig.versionCode + 10000
|
||||||
|
versionName defaultConfig.versionName + "_" + getDate()
|
||||||
|
dimension "default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
exclude 'META-INF/LICENSE'
|
||||||
|
exclude 'META-INF/NOTICE'
|
||||||
|
exclude 'META-INF/rxjava.properties'
|
||||||
|
}
|
||||||
|
lintOptions {
|
||||||
|
disable 'MissingTranslation'
|
||||||
|
disable 'InvalidPackage'
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||||
|
def requested = details.requested
|
||||||
|
if (requested.group == 'com.android.support') {
|
||||||
|
if (!requested.name.startsWith("multidex")) {
|
||||||
|
details.useVersion '27.1.1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getProperties(String fileName) {
|
||||||
|
final def Properties properties = new Properties()
|
||||||
|
def file = file(fileName)
|
||||||
|
if (file.exists()) {
|
||||||
|
file.withInputStream { stream -> properties.load(stream) }
|
||||||
|
}
|
||||||
|
return properties
|
||||||
|
}
|
||||||
|
|
||||||
|
static def getProperty(Properties properties, String name) {
|
||||||
|
return properties.getProperty(name) ?: "$name missing"
|
||||||
|
}
|
||||||
|
|
||||||
|
static def getDate() {
|
||||||
|
new Date().format('yyyyMMdd')
|
||||||
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
supportLibVersion = "27.1.1"
|
||||||
|
firebase = "11.8.0"
|
||||||
|
retrofit = "2.3.0"
|
||||||
|
butterKnife = "8.8.1"
|
||||||
|
materialDialog = "0.9.6.0"
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
|
implementation "com.android.support:appcompat-v7:$supportLibVersion"
|
||||||
|
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
|
||||||
|
implementation "com.android.support:gridlayout-v7:$supportLibVersion"
|
||||||
|
implementation "com.android.support:cardview-v7:$supportLibVersion"
|
||||||
|
implementation "com.android.support:palette-v7:$supportLibVersion"
|
||||||
|
implementation "com.android.support:design:$supportLibVersion"
|
||||||
|
implementation "com.android.support:support-annotations:$supportLibVersion"
|
||||||
|
implementation "com.android.support:preference-v7:$supportLibVersion"
|
||||||
|
implementation "com.android.support:preference-v14:$supportLibVersion"
|
||||||
|
implementation "com.squareup.retrofit2:retrofit:$retrofit"
|
||||||
|
implementation "com.squareup.retrofit2:converter-gson:$retrofit"
|
||||||
|
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit"
|
||||||
|
implementation "com.jakewharton:butterknife:$butterKnife"
|
||||||
|
annotationProcessor "com.jakewharton:butterknife-compiler:$butterKnife"
|
||||||
|
implementation "com.afollestad.material-dialogs:core:$materialDialog"
|
||||||
|
implementation "com.afollestad.material-dialogs:commons:$materialDialog"
|
||||||
|
implementation 'com.afollestad:material-cab:0.1.12'
|
||||||
|
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
|
||||||
|
implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
|
||||||
|
implementation 'com.github.bumptech.glide:glide:3.8.0'
|
||||||
|
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
|
||||||
|
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||||
|
implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0'
|
||||||
|
implementation('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.11.0@aar') {
|
||||||
|
transitive = true
|
||||||
|
}
|
||||||
|
implementation 'com.mpatric:mp3agic:0.8.3'
|
||||||
|
implementation 'com.github.ksoichiro:android-observablescrollview:1.6.0'
|
||||||
|
implementation 'com.github.kabouzeid:RecyclerView-FastScroll:1.0.16-kmod'
|
||||||
|
implementation 'com.anjlab.android.iab.v3:library:1.0.44'
|
||||||
|
implementation 'jp.wasabeef:glide-transformations:2.0.2'
|
||||||
|
/*UI Library*/
|
||||||
|
implementation 'com.github.jetradarmobile:android-snowfall:1.1.6'
|
||||||
|
implementation 'uk.co.chrisjenx:calligraphy:2.3.0'
|
||||||
|
implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2'
|
||||||
|
implementation 'com.r0adkll:slidableactivity:2.0.6'
|
||||||
|
/*Backend all*/
|
||||||
|
implementation project(':appthemehelper')
|
||||||
|
implementation 'com.sothree.slidinguppanel:library:3.4.0'
|
||||||
|
implementation 'org.nanohttpd:nanohttpd:2.3.1'
|
||||||
|
}
|
BIN
app/libs/jaudiotagger-android-2.2.3.jar
Normal file
BIN
app/libs/jaudiotagger-android-2.2.3.jar
Normal file
Binary file not shown.
BIN
app/libs/jsoup-1.11.2.jar
Normal file
BIN
app/libs/jsoup-1.11.2.jar
Normal file
Binary file not shown.
BIN
app/libs/juniversalchardet-1.0.3.jar
Normal file
BIN
app/libs/juniversalchardet-1.0.3.jar
Normal file
Binary file not shown.
75
app/proguard-rules.pro
vendored
Normal file
75
app/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in /Users/hemanths/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
-dontwarn java.lang.invoke.*
|
||||||
|
-dontwarn **$$Lambda$*
|
||||||
|
|
||||||
|
# RetroFit
|
||||||
|
-dontwarn retrofit.**
|
||||||
|
-keep class retrofit.** { *; }
|
||||||
|
-keepattributes Signature
|
||||||
|
-keepattributes Exceptions
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
|
# Glide
|
||||||
|
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||||
|
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||||
|
**[] $VALUES;
|
||||||
|
public *;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ButterKnife
|
||||||
|
-keep class butterknife.** { *; }
|
||||||
|
-dontwarn butterknife.internal.**
|
||||||
|
-keep class **$$ViewBinder { *; }
|
||||||
|
-keepclasseswithmembernames class * {
|
||||||
|
@butterknife.* <fields>;
|
||||||
|
}
|
||||||
|
-keepclasseswithmembernames class * {
|
||||||
|
@butterknife.* <methods>;
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep class !android.support.v7.internal.view.menu.**,** {*;}
|
||||||
|
|
||||||
|
-dontwarn
|
||||||
|
-ignorewarnings
|
||||||
|
|
||||||
|
# ------- FastScrollRecycleView START -------
|
||||||
|
-keep class com.simplecityapps.recyclerview_fastscroll.views.FastScrollPopup { *; }
|
||||||
|
# ------- FastScrollRecycleView END -------
|
||||||
|
|
||||||
|
-keep public class android.support.design.widget.BottomNavigationView { *; }
|
||||||
|
-keep public class android.support.design.internal.BottomNavigationMenuView { *; }
|
||||||
|
-keep public class android.support.design.internal.BottomNavigationPresenter { *; }
|
||||||
|
-keep public class android.support.design.internal.BottomNavigationItemView { *; }
|
||||||
|
|
||||||
|
#-dontwarn android.support.v8.renderscript.*
|
||||||
|
#-keepclassmembers class android.support.v8.renderscript.RenderScript {
|
||||||
|
# native *** rsn*(...);
|
||||||
|
# native *** n*(...);
|
||||||
|
#}
|
||||||
|
|
||||||
|
#-keep class org.jaudiotagger.** { *; }
|
244
app/src/main/AndroidManifest.xml
Normal file
244
app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="code.name.monkey.retromusic">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
<uses-permission android:name="com.android.vending.BILLING" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".RetroApplication"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.RetroMusic.Light"
|
||||||
|
tools:replace="android:allowBackup">
|
||||||
|
<activity
|
||||||
|
android:name=".ui.activities.MainActivity"
|
||||||
|
android:label="@string/app_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<action android:name="android.intent.action.MUSIC_PLAYER" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<category android:name="android.intent.category.APP_MUSIC" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:scheme="content" />
|
||||||
|
<data android:mimeType="audio/*" />
|
||||||
|
<data android:mimeType="application/ogg" />
|
||||||
|
<data android:mimeType="application/x-ogg" />
|
||||||
|
<data android:mimeType="application/itunes" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:scheme="file" />
|
||||||
|
<data android:mimeType="audio/*" />
|
||||||
|
<data android:mimeType="application/ogg" />
|
||||||
|
<data android:mimeType="application/x-ogg" />
|
||||||
|
<data android:mimeType="application/itunes" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="http" />
|
||||||
|
<data android:mimeType="audio/*" />
|
||||||
|
<data android:mimeType="application/ogg" />
|
||||||
|
<data android:mimeType="application/x-ogg" />
|
||||||
|
<data android:mimeType="application/itunes" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:mimeType="vnd.android.cursor.dir/playlist" />
|
||||||
|
<data android:mimeType="vnd.android.cursor.dir/albums" />
|
||||||
|
<data android:mimeType="vnd.android.cursor.dir/artists" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.cyanogenmod.eleven.AUDIO_PLAYER" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.PICK" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.OPENABLE" />
|
||||||
|
|
||||||
|
<data android:mimeType="vnd.android.cursor.dir/audio" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".ui.activities.AlbumDetailsActivity" />
|
||||||
|
<activity android:name=".ui.activities.ArtistDetailActivity" />
|
||||||
|
<activity android:name=".ui.activities.PlaylistDetailActivity" />
|
||||||
|
<activity android:name=".ui.activities.PlayingQueueActivity" />
|
||||||
|
<activity android:name=".ui.activities.AboutActivity" />
|
||||||
|
<activity android:name=".ui.activities.tageditor.AlbumTagEditorActivity" />
|
||||||
|
<activity android:name=".ui.activities.tageditor.SongTagEditorActivity" />
|
||||||
|
<activity android:name=".ui.activities.SettingsActivity" />
|
||||||
|
<activity android:name=".ui.activities.SearchActivity" />
|
||||||
|
<activity android:name=".ui.activities.LyricsActivity" />
|
||||||
|
<activity android:name=".ui.activities.UserInfoActivity" />
|
||||||
|
<activity
|
||||||
|
android:name=".appshortcuts.AppShortcutLauncherActivity"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.activities.LockScreenActivity"
|
||||||
|
android:autoRemoveFromRecents="true"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:screenOrientation="sensorPortrait"
|
||||||
|
android:showOnLockScreen="true" />
|
||||||
|
<activity android:name=".ui.activities.SupportDevelopmentActivity" />
|
||||||
|
<activity android:name=".ui.activities.GenreDetailsActivity" />
|
||||||
|
<activity android:name=".ui.activities.LicenseActivity" />
|
||||||
|
<activity android:name=".ui.activities.ProVersionActivity" />
|
||||||
|
<activity android:name=".ui.activities.EqualizerActivity" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".service.MusicService"
|
||||||
|
android:enabled="true" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".service.WearBrowserService"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.media.browse.MediaBrowserService" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!--<service-->
|
||||||
|
<!--android:name=".service.daydream.RetroMusicAlbums"-->
|
||||||
|
<!--android:exported="true"-->
|
||||||
|
<!--android:label="Retro Music Albums"-->
|
||||||
|
<!--android:permission="android.permission.BIND_DREAM_SERVICE">-->
|
||||||
|
<!--<intent-filter>-->
|
||||||
|
<!--<action android:name="android.service.dreams.DreamService" />-->
|
||||||
|
<!--<category android:name="android.intent.category.DEFAULT" />-->
|
||||||
|
<!--</intent-filter>-->
|
||||||
|
<!--</service>-->
|
||||||
|
<!-- Widgets -->
|
||||||
|
<!-- <receiver
|
||||||
|
android:name=".headset.HeadsetReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.HEADSET_PLUG" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>-->
|
||||||
|
<receiver android:name=".appwidgets.BootReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".appwidgets.AppWidgetBig"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/app_widget_big_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/app_widget_big_info" />
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".appwidgets.AppWidgetClassic"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/app_widget_classic_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/app_widget_classic_info" />
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".appwidgets.AppWidgetSmall"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/app_widget_small_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/app_widget_small_info" />
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".appwidgets.AppWidgetCard"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/app_widget_card_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/app_widget_card_info" />
|
||||||
|
</receiver>
|
||||||
|
<receiver android:name=".service.MediaButtonIntentReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<!--Widgets ends-->
|
||||||
|
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.max_aspect"
|
||||||
|
android:value="2.1" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.lge.support.SPLIT_WINDOW"
|
||||||
|
android:value="true" />
|
||||||
|
<meta-data
|
||||||
|
android:name="code.name.monkey.retromusic.glide.RetroMusicGlideModule"
|
||||||
|
android:value="GlideModule" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
|
||||||
|
android:value="GlideModule" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="android.support.v4.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
</manifest>
|
BIN
app/src/main/assets/fonts/circular_std_black.otf
Executable file
BIN
app/src/main/assets/fonts/circular_std_black.otf
Executable file
Binary file not shown.
BIN
app/src/main/assets/fonts/circular_std_book.otf
Executable file
BIN
app/src/main/assets/fonts/circular_std_book.otf
Executable file
Binary file not shown.
BIN
app/src/main/assets/fonts/product_sans_bold.ttf
Executable file
BIN
app/src/main/assets/fonts/product_sans_bold.ttf
Executable file
Binary file not shown.
BIN
app/src/main/assets/fonts/product_sans_regular.ttf
Executable file
BIN
app/src/main/assets/fonts/product_sans_regular.ttf
Executable file
Binary file not shown.
57
app/src/main/assets/index.html
Normal file
57
app/src/main/assets/index.html
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title></title>
|
||||||
|
<link rel="stylesheet" href="">
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #{bg-color};
|
||||||
|
color: #{text-color};
|
||||||
|
font-family: sans-serif;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #{license-bg-color};
|
||||||
|
padding: 1em;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="padding: 1.0rem 0.5rem 2.5rem 0.5rem;">
|
||||||
|
<p><b><a href="https://github.com/kabouzeid/Phonograph" title="Phonograph"> Phonograph</a></b> by Karim Abou Zeid</p>
|
||||||
|
<p><b><a href="https://github.com/ReactiveX/RxAndroid" title="RxAndroid"> RxAndroid</a></b> by RxAndroid authors</p>
|
||||||
|
<p><b><a href="https://github.com/ReactiveX/RxAndroid" title="RxJava"> RxJava</a></b> by RxJava authors</p>
|
||||||
|
<p> <b><a href="https://github.com/afollestad/material-cab" title="Material Dialogs"> Material Dialogs</a></b> by Aidan Michael Follestad</p>
|
||||||
|
<p><b><a href="https://github.com/chrisjenx/Calligraphy" title="Calligraphy"> Calligraphy</a></b> by RxJava authors</p>
|
||||||
|
<p><b><a href="https://github.com/JetradarMobile/android-snowfall" title="Android-Snowfall"> Android-Snowfall</a></b> by JetRadar</p>
|
||||||
|
<p><b><a href="https://github.com/umano/AndroidSlidingUpPanel" title="Android Sliding Up Panel"> Android Sliding Up Panel</a></b>by The Umano Team</p>
|
||||||
|
<p><b><a href="http://developer.android.com/tools/support-library/index.html" title="AOSP Support Libraries"> AOSP Support Libraries</a></b>by AOSP contributors</p>
|
||||||
|
<p><b><a href="https://github.com/JakeWharton/butterknife" title="Butter Knife"> Butter Knife</a></b> by Jake Wharton</p>
|
||||||
|
<p><b><a href="https://github.com/bumptech/glide" title="Glide"> Glide</a></b> by Sam Judd</p>
|
||||||
|
<p><b><a href="https://github.com/square/retrofit" title="Retrofit"> Retrofit</a></b> by Square team</p>
|
||||||
|
<p><b><a href="https://github.com/afollestad/material-cab" title="Material Contextual Action Bar"> Material Contextual Action Bar</a></b> by Aidan Michael Follestad</p>
|
||||||
|
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
|
||||||
|
<p><b><a href="https://github.com/hdodenhof/CircleImageView" title="CircleImageView"> CircleImageView</a></b> by Henning Dodenhof</p>
|
||||||
|
<p><b><a href="https://github.com/andkulikov/Transitions-Everywhere" title="Transitions Everywhere"> Transitions Everywhere</a></b> by Henning Dodenhof</p>
|
||||||
|
<p><b><a href="https://github.com/DreaminginCodeZH/MaterialProgressBar" title="MaterialProgressBar"> MaterialProgressBar</a></b> by Zhang Hai</p>
|
||||||
|
<p><b><a href="https://github.com/anjlab/android-inapp-billing-v3" title="Android In-App Billing v3 Library"> Android In-App Billing v3 Library</a></b> by Henning Dodenhof</p>
|
||||||
|
<p><b><a href="https://github.com/h6ah4i/android-advancedrecyclerview" title="Advanced RecyclerView"> Advanced RecyclerView</a></b> by Haruki Hasegawa</p>
|
||||||
|
<p><b><a href="https://github.com/ksoichiro/Android-ObservableScrollView" title="Android-ObservableScrollView"> Android-ObservableScrollView</a></b> by Soichiro Kashima</p>
|
||||||
|
<p><b><a href="https://github.com/ittianyu/BottomNavigationViewEx" title="BottomNavigationViewEx"> BottomNavigationViewEx</a></b> by Ittianyu</p>
|
||||||
|
<p><b><a href="https://github.com/ebanx/swipe-button" title="Swipe-Button"> Swipe-Button</a></b> by EBANX Team</p>
|
||||||
|
<p><b><a href="http://www.cufonfonts.com/es/font/14289/circular-std-book" title="Font used"> Font used(CIRCULAR STD BOOK FONT)</a></b> by NIELSON CAETANO</p>
|
||||||
|
<p><b><a href="https://materialdesignicons.com" title="Icons"> Icons</a></b> by Austin Andrews</p>
|
||||||
|
<p><b><a href="https://github.com/harjot-oberai/Croller" title="Croller"> Croller</a></b> by Harjot Oberai</p>
|
||||||
|
<p><b><a href="https://www.techjuice.pk" title="City wallpaper"> Material Design City Wallpaper</a></b></p>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
BIN
app/src/main/ic_launcher-web.png
Normal file
BIN
app/src/main/ic_launcher-web.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
48
app/src/main/java/code/name/monkey/retromusic/Constants.java
Normal file
48
app/src/main/java/code/name/monkey/retromusic/Constants.java
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package code.name.monkey.retromusic;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
|
||||||
|
public static final String DISCORD_LINK = "https://discord.gg/qTecXXn";
|
||||||
|
|
||||||
|
public static final String RETRO_MUSIC_PACKAGE_NAME = "code.name.monkey.retromusic";
|
||||||
|
public static final String MUSIC_PACKAGE_NAME = "com.android.music";
|
||||||
|
public static final String ACTION_TOGGLE_PAUSE = RETRO_MUSIC_PACKAGE_NAME + ".togglepause";
|
||||||
|
public static final String ACTION_PLAY = RETRO_MUSIC_PACKAGE_NAME + ".play";
|
||||||
|
public static final String ACTION_PLAY_PLAYLIST = RETRO_MUSIC_PACKAGE_NAME + ".play.playlist";
|
||||||
|
public static final String ACTION_PAUSE = RETRO_MUSIC_PACKAGE_NAME + ".pause";
|
||||||
|
public static final String ACTION_STOP = RETRO_MUSIC_PACKAGE_NAME + ".stop";
|
||||||
|
public static final String ACTION_SKIP = RETRO_MUSIC_PACKAGE_NAME + ".skip";
|
||||||
|
public static final String ACTION_REWIND = RETRO_MUSIC_PACKAGE_NAME + ".rewind";
|
||||||
|
public static final String ACTION_QUIT = RETRO_MUSIC_PACKAGE_NAME + ".quitservice";
|
||||||
|
public static final String INTENT_EXTRA_PLAYLIST =
|
||||||
|
RETRO_MUSIC_PACKAGE_NAME + "intentextra.playlist";
|
||||||
|
public static final String INTENT_EXTRA_SHUFFLE_MODE =
|
||||||
|
RETRO_MUSIC_PACKAGE_NAME + ".intentextra.shufflemode";
|
||||||
|
public static final String APP_WIDGET_UPDATE = RETRO_MUSIC_PACKAGE_NAME + ".appwidgetupdate";
|
||||||
|
public static final String EXTRA_APP_WIDGET_NAME = RETRO_MUSIC_PACKAGE_NAME + "app_widget_name";
|
||||||
|
// do not change these three strings as it will break support with other apps (e.g. last.fm scrobbling)
|
||||||
|
public static final String META_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".metachanged";
|
||||||
|
public static final String QUEUE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".queuechanged";
|
||||||
|
public static final String PLAY_STATE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".playstatechanged";
|
||||||
|
public static final String REPEAT_MODE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".repeatmodechanged";
|
||||||
|
public static final String SHUFFLE_MODE_CHANGED =
|
||||||
|
RETRO_MUSIC_PACKAGE_NAME + ".shufflemodechanged";
|
||||||
|
public static final String MEDIA_STORE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".mediastorechanged";
|
||||||
|
public static final String RATE_ON_GOOGLE_PLAY = "https://play.google.com/store/apps/details?id=code.name.monkey.retromusic";
|
||||||
|
public static final String LUIS_GOMZ_GOOGLE_PLUS = "https://plus.google.com/104046235912044592643";
|
||||||
|
public static final String LUIS_GOMZ_TWITTER = "https://www.twitter.com/LuisGmzz";
|
||||||
|
public static final String PAYPAL_ME_URL = "https://www.paypal.me/h4h14";
|
||||||
|
public static final String GOOGLE_PLUS_COMMUNITY = "https://plus.google.com/communities/110811566242871492162";
|
||||||
|
public static final String TRANSLATE = "http://monkeycodeapp.oneskyapp.com/collaboration/project?id=238534";
|
||||||
|
public static final String GITHUB_PROJECT = "https://github.com/h4h13/RetroMusicApp";
|
||||||
|
public static final String BASE_API_URL_KUGOU = "http://lyrics.kugou.com/";
|
||||||
|
public static final String TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog";
|
||||||
|
public static final String USER_PROFILE = "profile.jpg";
|
||||||
|
public static final String USER_BANNER = "banner.jpg";
|
||||||
|
public static final String APP_INSTAGRAM_LINK = "https://www.instagram.com/retromusicapp/";
|
||||||
|
public static final String APP_TELEGRAM_LINK = "https://t.me/retromusicapp/";
|
||||||
|
public static final String APP_TWITTER_LINK = "https://twitter.com/retromusicapp";
|
||||||
|
public static final int CAST_SERVER_PORT = 8080;
|
||||||
|
public static final String FAQ_LINK = "https://github.com/h4h13/RetroMusicApp/blob/master/FAQ.md";
|
||||||
|
public static String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||||
|
}
|
23
app/src/main/java/code/name/monkey/retromusic/Injection.java
Normal file
23
app/src/main/java/code/name/monkey/retromusic/Injection.java
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package code.name.monkey.retromusic;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.providers.RepositoryImpl;
|
||||||
|
import code.name.monkey.retromusic.providers.interfaces.Repository;
|
||||||
|
import code.name.monkey.retromusic.rest.KogouClient;
|
||||||
|
import code.name.monkey.retromusic.rest.service.KuGouApiService;
|
||||||
|
import code.name.monkey.retromusic.util.schedulers.BaseSchedulerProvider;
|
||||||
|
import code.name.monkey.retromusic.util.schedulers.SchedulerProvider;
|
||||||
|
|
||||||
|
public class Injection {
|
||||||
|
|
||||||
|
public static Repository provideRepository() {
|
||||||
|
return RepositoryImpl.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BaseSchedulerProvider provideSchedulerProvider() {
|
||||||
|
return SchedulerProvider.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KuGouApiService provideKuGouApiService() {
|
||||||
|
return new KogouClient().getApiService();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package code.name.monkey.retromusic;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.multidex.MultiDexApplication;
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager;
|
||||||
|
import com.anjlab.android.iab.v3.BillingProcessor;
|
||||||
|
import com.anjlab.android.iab.v3.TransactionDetails;
|
||||||
|
import uk.co.chrisjenx.calligraphy.CalligraphyConfig;
|
||||||
|
|
||||||
|
public class RetroApplication extends MultiDexApplication {
|
||||||
|
|
||||||
|
public static final String PRO_VERSION_PRODUCT_ID = "pro_version";
|
||||||
|
|
||||||
|
private static RetroApplication app;
|
||||||
|
|
||||||
|
private BillingProcessor billingProcessor;
|
||||||
|
|
||||||
|
public static RetroApplication getInstance() {
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isProVersion() {
|
||||||
|
return BuildConfig.DEBUG || app.billingProcessor.isPurchased(PRO_VERSION_PRODUCT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
app = this;
|
||||||
|
|
||||||
|
// default theme
|
||||||
|
if (!ThemeStore.isConfigured(this, 1)) {
|
||||||
|
ThemeStore.editTheme(this)
|
||||||
|
.accentColorRes(R.color.md_green_A200)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
|
||||||
|
.setDefaultFontPath("fonts/circular_std_book.otf")
|
||||||
|
.setFontAttrId(R.attr.fontPath)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
|
new DynamicShortcutManager(this).initDynamicShortcuts();
|
||||||
|
}
|
||||||
|
|
||||||
|
// automatically restores purchases
|
||||||
|
billingProcessor = new BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSE_KEY,
|
||||||
|
new BillingProcessor.IBillingHandler() {
|
||||||
|
@Override
|
||||||
|
public void onProductPurchased(@NonNull String productId, TransactionDetails details) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPurchaseHistoryRestored() {
|
||||||
|
//Toast.makeText(App.this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBillingError(int errorCode, Throwable error) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBillingInitialized() {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTerminate() {
|
||||||
|
super.onTerminate();
|
||||||
|
billingProcessor.release();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package code.name.monkey.retromusic.appshortcuts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.graphics.drawable.LayerDrawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Campos
|
||||||
|
*/
|
||||||
|
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
||||||
|
public final class AppShortcutIconGenerator {
|
||||||
|
public static Icon generateThemedIcon(Context context, int iconId) {
|
||||||
|
if (PreferenceUtil.getInstance(context).coloredAppShortcuts()){
|
||||||
|
return generateUserThemedIcon(context, iconId);
|
||||||
|
} else {
|
||||||
|
return generateDefaultThemedIcon(context, iconId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Icon generateDefaultThemedIcon(Context context, int iconId) {
|
||||||
|
// Return an Icon of iconId with default colors
|
||||||
|
return generateThemedIcon(context, iconId,
|
||||||
|
context.getColor(R.color.app_shortcut_default_foreground),
|
||||||
|
context.getColor(R.color.app_shortcut_default_background)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Icon generateUserThemedIcon(Context context, int iconId) {
|
||||||
|
// Get background color from context's theme
|
||||||
|
final TypedValue typedColorBackground = new TypedValue();
|
||||||
|
context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedColorBackground, true);
|
||||||
|
|
||||||
|
// Return an Icon of iconId with those colors
|
||||||
|
return generateThemedIcon(context, iconId,
|
||||||
|
ThemeStore.accentColor(context),
|
||||||
|
typedColorBackground.data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Icon generateThemedIcon(Context context, int iconId, int foregroundColor, int backgroundColor) {
|
||||||
|
// Get and tint foreground and background drawables
|
||||||
|
Drawable vectorDrawable = RetroUtil.getTintedVectorDrawable(context, iconId, foregroundColor);
|
||||||
|
Drawable backgroundDrawable = RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_app_shortcut_background, backgroundColor);
|
||||||
|
|
||||||
|
// Squash the two drawables together
|
||||||
|
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{backgroundDrawable, vectorDrawable});
|
||||||
|
|
||||||
|
// Return as an Icon
|
||||||
|
return Icon.createWithBitmap(drawableToBitmap(layerDrawable));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap drawableToBitmap(Drawable drawable) {
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||||
|
drawable.draw(canvas);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package code.name.monkey.retromusic.appshortcuts;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist;
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist;
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.ShuffleAllPlaylist;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType;
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
|
||||||
|
import static code.name.monkey.retromusic.Constants.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Campos
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class AppShortcutLauncherActivity extends Activity {
|
||||||
|
public static final String KEY_SHORTCUT_TYPE = "code.name.monkey.retromusic.appshortcuts.ShortcutType";
|
||||||
|
|
||||||
|
public static final int SHORTCUT_TYPE_SHUFFLE_ALL = 0;
|
||||||
|
public static final int SHORTCUT_TYPE_TOP_TRACKS = 1;
|
||||||
|
public static final int SHORTCUT_TYPE_LAST_ADDED = 2;
|
||||||
|
public static final int SHORTCUT_TYPE_NONE = 3;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
int shortcutType = SHORTCUT_TYPE_NONE;
|
||||||
|
|
||||||
|
// Set shortcutType from the intent extras
|
||||||
|
Bundle extras = getIntent().getExtras();
|
||||||
|
if (extras != null) {
|
||||||
|
//noinspection WrongConstant
|
||||||
|
shortcutType = extras.getInt(KEY_SHORTCUT_TYPE, SHORTCUT_TYPE_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (shortcutType) {
|
||||||
|
case SHORTCUT_TYPE_SHUFFLE_ALL:
|
||||||
|
startServiceWithPlaylist(MusicService.SHUFFLE_MODE_SHUFFLE,
|
||||||
|
new ShuffleAllPlaylist(getApplicationContext()));
|
||||||
|
DynamicShortcutManager.reportShortcutUsed(this, ShuffleAllShortcutType.getId());
|
||||||
|
break;
|
||||||
|
case SHORTCUT_TYPE_TOP_TRACKS:
|
||||||
|
startServiceWithPlaylist(MusicService.SHUFFLE_MODE_NONE,
|
||||||
|
new MyTopTracksPlaylist(getApplicationContext()));
|
||||||
|
DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.getId());
|
||||||
|
break;
|
||||||
|
case SHORTCUT_TYPE_LAST_ADDED:
|
||||||
|
startServiceWithPlaylist(MusicService.SHUFFLE_MODE_NONE,
|
||||||
|
new LastAddedPlaylist(getApplicationContext()));
|
||||||
|
DynamicShortcutManager.reportShortcutUsed(this, LastAddedShortcutType.getId());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startServiceWithPlaylist(int shuffleMode, Playlist playlist) {
|
||||||
|
Intent intent = new Intent(this, MusicService.class);
|
||||||
|
intent.setAction(ACTION_PLAY_PLAYLIST);
|
||||||
|
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(INTENT_EXTRA_PLAYLIST, playlist);
|
||||||
|
bundle.putInt(INTENT_EXTRA_SHUFFLE_MODE, shuffleMode);
|
||||||
|
|
||||||
|
intent.putExtras(bundle);
|
||||||
|
|
||||||
|
startService(intent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package code.name.monkey.retromusic.appshortcuts;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.content.pm.ShortcutManager;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Campos
|
||||||
|
*/
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||||
|
public class DynamicShortcutManager {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private ShortcutManager shortcutManager;
|
||||||
|
|
||||||
|
public DynamicShortcutManager(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
shortcutManager = this.context.getSystemService(ShortcutManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShortcutInfo createShortcut(Context context, String id, String shortLabel, String longLabel, Icon icon, Intent intent) {
|
||||||
|
return new ShortcutInfo.Builder(context, id)
|
||||||
|
.setShortLabel(shortLabel)
|
||||||
|
.setLongLabel(longLabel)
|
||||||
|
.setIcon(icon)
|
||||||
|
.setIntent(intent)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initDynamicShortcuts() {
|
||||||
|
if (shortcutManager.getDynamicShortcuts().size() == 0) {
|
||||||
|
shortcutManager.setDynamicShortcuts(getDefaultShortcuts());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateDynamicShortcuts() {
|
||||||
|
shortcutManager.updateShortcuts(getDefaultShortcuts());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ShortcutInfo> getDefaultShortcuts() {
|
||||||
|
return (Arrays.asList(
|
||||||
|
new ShuffleAllShortcutType(context).getShortcutInfo(),
|
||||||
|
new TopTracksShortcutType(context).getShortcutInfo(),
|
||||||
|
new LastAddedShortcutType(context).getShortcutInfo()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reportShortcutUsed(Context context, String shortcutId){
|
||||||
|
context.getSystemService(ShortcutManager.class).reportShortcutUsed(shortcutId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package code.name.monkey.retromusic.appshortcuts.shortcuttype;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Campos
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||||
|
public abstract class BaseShortcutType {
|
||||||
|
|
||||||
|
static final String ID_PREFIX = "code.name.monkey.retromusic.appshortcuts.id.";
|
||||||
|
|
||||||
|
Context context;
|
||||||
|
|
||||||
|
public BaseShortcutType(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public String getId() {
|
||||||
|
return ID_PREFIX + "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract ShortcutInfo getShortcutInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Intent that will launch MainActivtiy and immediately play {@param songs} in either shuffle or normal mode
|
||||||
|
*
|
||||||
|
* @param shortcutType Describes the type of shortcut to create (ShuffleAll, TopTracks, custom playlist, etc.)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Intent getPlaySongsIntent(int shortcutType) {
|
||||||
|
Intent intent = new Intent(context, AppShortcutLauncherActivity.class);
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putInt(AppShortcutLauncherActivity.KEY_SHORTCUT_TYPE, shortcutType);
|
||||||
|
|
||||||
|
intent.putExtras(b);
|
||||||
|
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package code.name.monkey.retromusic.appshortcuts.shortcuttype;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Campos
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||||
|
public final class LastAddedShortcutType extends BaseShortcutType {
|
||||||
|
public LastAddedShortcutType(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getId() {
|
||||||
|
return ID_PREFIX + "last_added";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShortcutInfo getShortcutInfo() {
|
||||||
|
return new ShortcutInfo.Builder(context, getId())
|
||||||
|
.setShortLabel(context.getString(R.string.app_shortcut_last_added_short))
|
||||||
|
.setLongLabel(context.getString(R.string.app_shortcut_last_added_long))
|
||||||
|
.setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_last_added))
|
||||||
|
.setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_LAST_ADDED))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package code.name.monkey.retromusic.appshortcuts.shortcuttype;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Campos
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||||
|
public final class ShuffleAllShortcutType extends BaseShortcutType {
|
||||||
|
public ShuffleAllShortcutType(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getId() {
|
||||||
|
return ID_PREFIX + "shuffle_all";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShortcutInfo getShortcutInfo() {
|
||||||
|
return new ShortcutInfo.Builder(context, getId())
|
||||||
|
.setShortLabel(context.getString(R.string.app_shortcut_shuffle_all_short))
|
||||||
|
.setLongLabel(context.getString(R.string.app_shortcut_shuffle_all_long))
|
||||||
|
.setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_shuffle_all))
|
||||||
|
.setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_SHUFFLE_ALL))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package code.name.monkey.retromusic.appshortcuts.shortcuttype;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator;
|
||||||
|
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Campos
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||||
|
public final class TopTracksShortcutType extends BaseShortcutType {
|
||||||
|
public TopTracksShortcutType(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getId() {
|
||||||
|
return ID_PREFIX + "top_tracks";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShortcutInfo getShortcutInfo() {
|
||||||
|
return new ShortcutInfo.Builder(context, getId())
|
||||||
|
.setShortLabel(context.getString(R.string.app_shortcut_top_tracks_short))
|
||||||
|
.setLongLabel(context.getString(R.string.app_shortcut_top_tracks_long))
|
||||||
|
.setIcon(AppShortcutIconGenerator.generateThemedIcon(context, R.drawable.ic_app_shortcut_top_tracks))
|
||||||
|
.setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_TOP_TRACKS))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
package code.name.monkey.retromusic.appwidgets;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
||||||
|
import code.name.monkey.retromusic.Constants;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget;
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
import code.name.monkey.retromusic.ui.activities.MainActivity;
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
|
import com.bumptech.glide.request.target.Target;
|
||||||
|
|
||||||
|
public class AppWidgetBig extends BaseAppWidget {
|
||||||
|
|
||||||
|
public static final String NAME = "app_widget_big";
|
||||||
|
|
||||||
|
private static AppWidgetBig mInstance;
|
||||||
|
private Target<Bitmap> target; // for cancellation
|
||||||
|
|
||||||
|
public static synchronized AppWidgetBig getInstance() {
|
||||||
|
if (mInstance == null) {
|
||||||
|
mInstance = new AppWidgetBig();
|
||||||
|
}
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||||
|
* actions if service not running.
|
||||||
|
*/
|
||||||
|
protected void defaultAppWidget(final Context context, final int[] appWidgetIds) {
|
||||||
|
final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(),
|
||||||
|
R.layout.app_widget_big);
|
||||||
|
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||||
|
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art);
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
MaterialValueHelper.getPrimaryTextColor(context, false)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
MaterialValueHelper.getPrimaryTextColor(context, false)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp,
|
||||||
|
MaterialValueHelper.getPrimaryTextColor(context, false)), 1f));
|
||||||
|
|
||||||
|
linkButtons(context, appWidgetView);
|
||||||
|
pushUpdate(context, appWidgetIds, appWidgetView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update all active widget instances by pushing changes
|
||||||
|
*/
|
||||||
|
public void performUpdate(final MusicService service, final int[] appWidgetIds) {
|
||||||
|
final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(),
|
||||||
|
R.layout.app_widget_big);
|
||||||
|
|
||||||
|
final boolean isPlaying = service.isPlaying();
|
||||||
|
final Song song = service.getCurrentSong();
|
||||||
|
|
||||||
|
// Set the titles and artwork
|
||||||
|
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||||
|
} else {
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE);
|
||||||
|
appWidgetView.setTextViewText(R.id.title, song.title);
|
||||||
|
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set correct drawable for pause state
|
||||||
|
int playPauseRes =
|
||||||
|
isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp;
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, playPauseRes,
|
||||||
|
MaterialValueHelper.getPrimaryTextColor(service, false)), 1f));
|
||||||
|
|
||||||
|
// Set prev/next button drawables
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
MaterialValueHelper.getPrimaryTextColor(service, false)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
MaterialValueHelper.getPrimaryTextColor(service, false)), 1f));
|
||||||
|
|
||||||
|
// Link actions buttons to intents
|
||||||
|
linkButtons(service, appWidgetView);
|
||||||
|
|
||||||
|
// Load the album cover async and push the update on completion
|
||||||
|
Point p = RetroUtil.getScreenSize(service);
|
||||||
|
final int widgetImageSize = Math.min(p.x, p.y);
|
||||||
|
final Context appContext = service.getApplicationContext();
|
||||||
|
service.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (target != null) {
|
||||||
|
Glide.clear(target);
|
||||||
|
}
|
||||||
|
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
|
||||||
|
.checkIgnoreMediaStore(appContext)
|
||||||
|
.asBitmap().build()
|
||||||
|
.into(new SimpleTarget<Bitmap>(widgetImageSize, widgetImageSize) {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(Bitmap resource,
|
||||||
|
GlideAnimation<? super Bitmap> glideAnimation) {
|
||||||
|
update(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFailed(Exception e, Drawable errorDrawable) {
|
||||||
|
super.onLoadFailed(e, errorDrawable);
|
||||||
|
update(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(@Nullable Bitmap bitmap) {
|
||||||
|
if (bitmap == null) {
|
||||||
|
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art);
|
||||||
|
} else {
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.image, bitmap);
|
||||||
|
}
|
||||||
|
pushUpdate(appContext, appWidgetIds, appWidgetView);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link up various button actions using {@link PendingIntent}.
|
||||||
|
*/
|
||||||
|
private void linkButtons(final Context context, final RemoteViews views) {
|
||||||
|
Intent action;
|
||||||
|
PendingIntent pendingIntent;
|
||||||
|
|
||||||
|
final ComponentName serviceName = new ComponentName(context, MusicService.class);
|
||||||
|
|
||||||
|
// Home
|
||||||
|
action = new Intent(context, MainActivity.class);
|
||||||
|
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
pendingIntent = PendingIntent.getActivity(context, 0, action, 0);
|
||||||
|
views.setOnClickPendingIntent(R.id.clickable_area, pendingIntent);
|
||||||
|
|
||||||
|
// Previous track
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent);
|
||||||
|
|
||||||
|
// Play and pause
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent);
|
||||||
|
|
||||||
|
// Next track
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_next, pendingIntent);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,194 @@
|
||||||
|
package code.name.monkey.retromusic.appwidgets;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.graphics.Palette;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
||||||
|
import code.name.monkey.retromusic.Constants;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget;
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
import code.name.monkey.retromusic.ui.activities.MainActivity;
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
|
import com.bumptech.glide.request.target.Target;
|
||||||
|
|
||||||
|
public class AppWidgetCard extends BaseAppWidget {
|
||||||
|
|
||||||
|
public static final String NAME = "app_widget_card";
|
||||||
|
|
||||||
|
private static AppWidgetCard mInstance;
|
||||||
|
private static int imageSize = 0;
|
||||||
|
private static float cardRadius = 0f;
|
||||||
|
private Target<BitmapPaletteWrapper> target; // for cancellation
|
||||||
|
|
||||||
|
public static synchronized AppWidgetCard getInstance() {
|
||||||
|
if (mInstance == null) {
|
||||||
|
mInstance = new AppWidgetCard();
|
||||||
|
}
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||||
|
* actions if service not running.
|
||||||
|
*/
|
||||||
|
protected void defaultAppWidget(final Context context, final int[] appWidgetIds) {
|
||||||
|
final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(),
|
||||||
|
R.layout.app_widget_card);
|
||||||
|
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||||
|
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art);
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
|
||||||
|
linkButtons(context, appWidgetView);
|
||||||
|
pushUpdate(context, appWidgetIds, appWidgetView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update all active widget instances by pushing changes
|
||||||
|
*/
|
||||||
|
public void performUpdate(final MusicService service, final int[] appWidgetIds) {
|
||||||
|
final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(),
|
||||||
|
R.layout.app_widget_card);
|
||||||
|
|
||||||
|
final boolean isPlaying = service.isPlaying();
|
||||||
|
final Song song = service.getCurrentSong();
|
||||||
|
|
||||||
|
// Set the titles and artwork
|
||||||
|
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||||
|
} else {
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE);
|
||||||
|
appWidgetView.setTextViewText(R.id.title, song.title);
|
||||||
|
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set correct drawable for pause state
|
||||||
|
int playPauseRes =
|
||||||
|
isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp;
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, playPauseRes,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(service, true)), 1f));
|
||||||
|
|
||||||
|
// Set prev/next button drawables
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(service, true)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(service, true)), 1f));
|
||||||
|
|
||||||
|
// Link actions buttons to intents
|
||||||
|
linkButtons(service, appWidgetView);
|
||||||
|
|
||||||
|
if (imageSize == 0) {
|
||||||
|
imageSize = service.getResources().getDimensionPixelSize(R.dimen.app_widget_card_image_size);
|
||||||
|
}
|
||||||
|
if (cardRadius == 0f) {
|
||||||
|
cardRadius = service.getResources().getDimension(R.dimen.app_widget_card_radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the album cover async and push the update on completion
|
||||||
|
service.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (target != null) {
|
||||||
|
Glide.clear(target);
|
||||||
|
}
|
||||||
|
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||||
|
.checkIgnoreMediaStore(service)
|
||||||
|
.generatePalette(service).build()
|
||||||
|
.centerCrop()
|
||||||
|
.into(new SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(BitmapPaletteWrapper resource,
|
||||||
|
GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
|
||||||
|
Palette palette = resource.getPalette();
|
||||||
|
update(resource.getBitmap(), palette.getVibrantColor(palette
|
||||||
|
.getMutedColor(MaterialValueHelper.getSecondaryTextColor(service, true))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFailed(Exception e, Drawable errorDrawable) {
|
||||||
|
super.onLoadFailed(e, errorDrawable);
|
||||||
|
update(null, MaterialValueHelper.getSecondaryTextColor(service, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(@Nullable Bitmap bitmap, int color) {
|
||||||
|
// Set correct drawable for pause state
|
||||||
|
int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp
|
||||||
|
: R.drawable.ic_play_arrow_white_24dp;
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause,
|
||||||
|
createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRes, color), 1f));
|
||||||
|
|
||||||
|
// Set prev/next button drawables
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
color), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
color), 1f));
|
||||||
|
|
||||||
|
final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap);
|
||||||
|
final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize,
|
||||||
|
cardRadius, 0, cardRadius, 0);
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap);
|
||||||
|
|
||||||
|
pushUpdate(service, appWidgetIds, appWidgetView);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link up various button actions using {@link PendingIntent}.
|
||||||
|
*/
|
||||||
|
private void linkButtons(final Context context, final RemoteViews views) {
|
||||||
|
Intent action;
|
||||||
|
PendingIntent pendingIntent;
|
||||||
|
|
||||||
|
final ComponentName serviceName = new ComponentName(context, MusicService.class);
|
||||||
|
|
||||||
|
// Home
|
||||||
|
action = new Intent(context, MainActivity.class);
|
||||||
|
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
pendingIntent = PendingIntent.getActivity(context, 0, action, 0);
|
||||||
|
views.setOnClickPendingIntent(R.id.image, pendingIntent);
|
||||||
|
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent);
|
||||||
|
|
||||||
|
// Previous track
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent);
|
||||||
|
|
||||||
|
// Play and pause
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent);
|
||||||
|
|
||||||
|
// Next track
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_next, pendingIntent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
package code.name.monkey.retromusic.appwidgets;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.graphics.Palette;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
||||||
|
import code.name.monkey.retromusic.Constants;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget;
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
import code.name.monkey.retromusic.ui.activities.MainActivity;
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
|
import com.bumptech.glide.request.target.Target;
|
||||||
|
|
||||||
|
public class AppWidgetClassic extends BaseAppWidget {
|
||||||
|
|
||||||
|
public static final String NAME = "app_widget_classic";
|
||||||
|
|
||||||
|
private static AppWidgetClassic mInstance;
|
||||||
|
private static int imageSize = 0;
|
||||||
|
private static float cardRadius = 0f;
|
||||||
|
private Target<BitmapPaletteWrapper> target; // for cancellation
|
||||||
|
|
||||||
|
public static synchronized AppWidgetClassic getInstance() {
|
||||||
|
if (mInstance == null) {
|
||||||
|
mInstance = new AppWidgetClassic();
|
||||||
|
}
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||||
|
* actions if service not running.
|
||||||
|
*/
|
||||||
|
protected void defaultAppWidget(final Context context, final int[] appWidgetIds) {
|
||||||
|
final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(),
|
||||||
|
R.layout.app_widget_classic);
|
||||||
|
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||||
|
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art);
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
|
||||||
|
linkButtons(context, appWidgetView);
|
||||||
|
pushUpdate(context, appWidgetIds, appWidgetView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update all active widget instances by pushing changes
|
||||||
|
*/
|
||||||
|
public void performUpdate(final MusicService service, final int[] appWidgetIds) {
|
||||||
|
final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(),
|
||||||
|
R.layout.app_widget_classic);
|
||||||
|
|
||||||
|
final boolean isPlaying = service.isPlaying();
|
||||||
|
final Song song = service.getCurrentSong();
|
||||||
|
|
||||||
|
// Set the titles and artwork
|
||||||
|
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||||
|
} else {
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE);
|
||||||
|
appWidgetView.setTextViewText(R.id.title, song.title);
|
||||||
|
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link actions buttons to intents
|
||||||
|
linkButtons(service, appWidgetView);
|
||||||
|
|
||||||
|
if (imageSize == 0) {
|
||||||
|
imageSize = service.getResources()
|
||||||
|
.getDimensionPixelSize(R.dimen.app_widget_classic_image_size);
|
||||||
|
}
|
||||||
|
if (cardRadius == 0f) {
|
||||||
|
cardRadius = service.getResources().getDimension(R.dimen.app_widget_card_radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the album cover async and push the update on completion
|
||||||
|
final Context appContext = service.getApplicationContext();
|
||||||
|
service.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (target != null) {
|
||||||
|
Glide.clear(target);
|
||||||
|
}
|
||||||
|
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
|
||||||
|
.checkIgnoreMediaStore(appContext)
|
||||||
|
.generatePalette(service).build()
|
||||||
|
.centerCrop()
|
||||||
|
.into(new SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(BitmapPaletteWrapper resource,
|
||||||
|
GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
|
||||||
|
Palette palette = resource.getPalette();
|
||||||
|
update(resource.getBitmap(), palette.getVibrantColor(palette
|
||||||
|
.getMutedColor(MaterialValueHelper.getSecondaryTextColor(appContext, true))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFailed(Exception e, Drawable errorDrawable) {
|
||||||
|
super.onLoadFailed(e, errorDrawable);
|
||||||
|
update(null, MaterialValueHelper.getSecondaryTextColor(appContext, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(@Nullable Bitmap bitmap, int color) {
|
||||||
|
// Set correct drawable for pause state
|
||||||
|
int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp
|
||||||
|
: R.drawable.ic_play_arrow_white_24dp;
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause,
|
||||||
|
createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRes, color), 1f));
|
||||||
|
|
||||||
|
// Set prev/next button drawables
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
color), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
color), 1f));
|
||||||
|
|
||||||
|
final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap);
|
||||||
|
final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize,
|
||||||
|
cardRadius, 0, cardRadius, 0);
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap);
|
||||||
|
|
||||||
|
pushUpdate(appContext, appWidgetIds, appWidgetView);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link up various button actions using {@link PendingIntent}.
|
||||||
|
*/
|
||||||
|
private void linkButtons(final Context context, final RemoteViews views) {
|
||||||
|
Intent action;
|
||||||
|
PendingIntent pendingIntent;
|
||||||
|
|
||||||
|
final ComponentName serviceName = new ComponentName(context, MusicService.class);
|
||||||
|
|
||||||
|
// Home
|
||||||
|
action = new Intent(context, MainActivity.class);
|
||||||
|
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
pendingIntent = PendingIntent.getActivity(context, 0, action, 0);
|
||||||
|
views.setOnClickPendingIntent(R.id.image, pendingIntent);
|
||||||
|
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent);
|
||||||
|
|
||||||
|
// Previous track
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent);
|
||||||
|
|
||||||
|
// Play and pause
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent);
|
||||||
|
|
||||||
|
// Next track
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_next, pendingIntent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
package code.name.monkey.retromusic.appwidgets;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.graphics.Palette;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
||||||
|
import code.name.monkey.retromusic.Constants;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget;
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
import code.name.monkey.retromusic.ui.activities.MainActivity;
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
|
import com.bumptech.glide.request.target.Target;
|
||||||
|
|
||||||
|
public class AppWidgetSmall extends BaseAppWidget {
|
||||||
|
|
||||||
|
public static final String NAME = "app_widget_small";
|
||||||
|
|
||||||
|
private static AppWidgetSmall mInstance;
|
||||||
|
private static int imageSize = 0;
|
||||||
|
private static float cardRadius = 0f;
|
||||||
|
private Target<BitmapPaletteWrapper> target; // for cancellation
|
||||||
|
|
||||||
|
public static synchronized AppWidgetSmall getInstance() {
|
||||||
|
if (mInstance == null) {
|
||||||
|
mInstance = new AppWidgetSmall();
|
||||||
|
}
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||||
|
* actions if service not running.
|
||||||
|
*/
|
||||||
|
protected void defaultAppWidget(final Context context, final int[] appWidgetIds) {
|
||||||
|
final RemoteViews appWidgetView = new RemoteViews(context.getPackageName(),
|
||||||
|
R.layout.app_widget_small);
|
||||||
|
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||||
|
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art);
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp,
|
||||||
|
MaterialValueHelper.getSecondaryTextColor(context, true)), 1f));
|
||||||
|
|
||||||
|
linkButtons(context, appWidgetView);
|
||||||
|
pushUpdate(context, appWidgetIds, appWidgetView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update all active widget instances by pushing changes
|
||||||
|
*/
|
||||||
|
public void performUpdate(final MusicService service, final int[] appWidgetIds) {
|
||||||
|
final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(),
|
||||||
|
R.layout.app_widget_small);
|
||||||
|
|
||||||
|
final boolean isPlaying = service.isPlaying();
|
||||||
|
final Song song = service.getCurrentSong();
|
||||||
|
|
||||||
|
// Set the titles and artwork
|
||||||
|
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||||
|
} else {
|
||||||
|
if (TextUtils.isEmpty(song.title) || TextUtils.isEmpty(song.artistName)) {
|
||||||
|
appWidgetView.setTextViewText(R.id.text_separator, "");
|
||||||
|
} else {
|
||||||
|
appWidgetView.setTextViewText(R.id.text_separator, "•");
|
||||||
|
}
|
||||||
|
|
||||||
|
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE);
|
||||||
|
appWidgetView.setTextViewText(R.id.title, song.title);
|
||||||
|
appWidgetView.setTextViewText(R.id.text, song.artistName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link actions buttons to intents
|
||||||
|
linkButtons(service, appWidgetView);
|
||||||
|
|
||||||
|
if (imageSize == 0) {
|
||||||
|
imageSize = service.getResources().getDimensionPixelSize(R.dimen.app_widget_small_image_size);
|
||||||
|
}
|
||||||
|
if (cardRadius == 0f) {
|
||||||
|
cardRadius = service.getResources().getDimension(R.dimen.app_widget_card_radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the album cover async and push the update on completion
|
||||||
|
final Context appContext = service.getApplicationContext();
|
||||||
|
service.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (target != null) {
|
||||||
|
Glide.clear(target);
|
||||||
|
}
|
||||||
|
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
|
||||||
|
.checkIgnoreMediaStore(appContext)
|
||||||
|
.generatePalette(service).build()
|
||||||
|
.centerCrop()
|
||||||
|
.into(new SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(BitmapPaletteWrapper resource,
|
||||||
|
GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
|
||||||
|
Palette palette = resource.getPalette();
|
||||||
|
update(resource.getBitmap(), palette.getVibrantColor(palette
|
||||||
|
.getMutedColor(MaterialValueHelper.getSecondaryTextColor(appContext, true))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFailed(Exception e, Drawable errorDrawable) {
|
||||||
|
super.onLoadFailed(e, errorDrawable);
|
||||||
|
update(null, MaterialValueHelper.getSecondaryTextColor(appContext, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(@Nullable Bitmap bitmap, int color) {
|
||||||
|
// Set correct drawable for pause state
|
||||||
|
int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp
|
||||||
|
: R.drawable.ic_play_arrow_white_24dp;
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause,
|
||||||
|
createBitmap(RetroUtil.getTintedVectorDrawable(service, playPauseRes, color), 1f));
|
||||||
|
|
||||||
|
// Set prev/next button drawables
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
|
||||||
|
color), 1f));
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(
|
||||||
|
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
|
||||||
|
color), 1f));
|
||||||
|
|
||||||
|
final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap);
|
||||||
|
final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize,
|
||||||
|
cardRadius, 0, 0, 0);
|
||||||
|
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap);
|
||||||
|
|
||||||
|
pushUpdate(appContext, appWidgetIds, appWidgetView);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link up various button actions using {@link PendingIntent}.
|
||||||
|
*/
|
||||||
|
private void linkButtons(final Context context, final RemoteViews views) {
|
||||||
|
Intent action;
|
||||||
|
PendingIntent pendingIntent;
|
||||||
|
|
||||||
|
final ComponentName serviceName = new ComponentName(context, MusicService.class);
|
||||||
|
|
||||||
|
// Home
|
||||||
|
action = new Intent(context, MainActivity.class);
|
||||||
|
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
pendingIntent = PendingIntent.getActivity(context, 0, action, 0);
|
||||||
|
views.setOnClickPendingIntent(R.id.image, pendingIntent);
|
||||||
|
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent);
|
||||||
|
|
||||||
|
// Previous track
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_REWIND, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent);
|
||||||
|
|
||||||
|
// Play and pause
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_TOGGLE_PAUSE, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent);
|
||||||
|
|
||||||
|
// Next track
|
||||||
|
pendingIntent = buildPendingIntent(context, Constants.ACTION_SKIP, serviceName);
|
||||||
|
views.setOnClickPendingIntent(R.id.button_next, pendingIntent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package code.name.monkey.retromusic.appwidgets;
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eugene Cheung (arkon)
|
||||||
|
*/
|
||||||
|
public class BootReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
final AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
|
||||||
|
|
||||||
|
// Start music service if there are any existing widgets
|
||||||
|
if (widgetManager.getAppWidgetIds(new ComponentName(context, AppWidgetBig.class)).length > 0 ||
|
||||||
|
widgetManager.getAppWidgetIds(new ComponentName(context, AppWidgetClassic.class)).length > 0 ||
|
||||||
|
widgetManager.getAppWidgetIds(new ComponentName(context, AppWidgetSmall.class)).length > 0 ||
|
||||||
|
widgetManager.getAppWidgetIds(new ComponentName(context, AppWidgetCard.class)).length > 0) {
|
||||||
|
final Intent serviceIntent = new Intent(context, MusicService.class);
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // not allowed on Oreo
|
||||||
|
context.startService(serviceIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package code.name.monkey.retromusic.appwidgets.base;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.appwidget.AppWidgetProvider;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapShader;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import code.name.monkey.retromusic.Constants;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
|
||||||
|
public abstract class BaseAppWidget extends AppWidgetProvider {
|
||||||
|
|
||||||
|
public static final String NAME = "app_widget";
|
||||||
|
|
||||||
|
protected static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) {
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * sizeMultiplier),
|
||||||
|
(int) (drawable.getIntrinsicHeight() * sizeMultiplier), Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas c = new Canvas(bitmap);
|
||||||
|
drawable.setBounds(0, 0, c.getWidth(), c.getHeight());
|
||||||
|
drawable.draw(c);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Bitmap createRoundedBitmap(Drawable drawable, int width, int height, float tl,
|
||||||
|
float tr, float bl, float br) {
|
||||||
|
if (drawable == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas c = new Canvas(bitmap);
|
||||||
|
drawable.setBounds(0, 0, width, height);
|
||||||
|
drawable.draw(c);
|
||||||
|
|
||||||
|
Bitmap rounded = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
|
||||||
|
Canvas canvas = new Canvas(rounded);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setShader(
|
||||||
|
new BitmapShader(bitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
canvas.drawPath(composeRoundedRectPath(new RectF(0, 0, width, height), tl, tr, bl, br), paint);
|
||||||
|
|
||||||
|
return rounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Path composeRoundedRectPath(RectF rect, float tl, float tr, float bl, float br) {
|
||||||
|
Path path = new Path();
|
||||||
|
tl = tl < 0 ? 0 : tl;
|
||||||
|
tr = tr < 0 ? 0 : tr;
|
||||||
|
bl = bl < 0 ? 0 : bl;
|
||||||
|
br = br < 0 ? 0 : br;
|
||||||
|
|
||||||
|
path.moveTo(rect.left + tl, rect.top);
|
||||||
|
path.lineTo(rect.right - tr, rect.top);
|
||||||
|
path.quadTo(rect.right, rect.top, rect.right, rect.top + tr);
|
||||||
|
path.lineTo(rect.right, rect.bottom - br);
|
||||||
|
path.quadTo(rect.right, rect.bottom, rect.right - br, rect.bottom);
|
||||||
|
path.lineTo(rect.left + bl, rect.bottom);
|
||||||
|
path.quadTo(rect.left, rect.bottom, rect.left, rect.bottom - bl);
|
||||||
|
path.lineTo(rect.left, rect.top + tl);
|
||||||
|
path.quadTo(rect.left, rect.top, rect.left + tl, rect.top);
|
||||||
|
path.close();
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onUpdate(final Context context, final AppWidgetManager appWidgetManager,
|
||||||
|
final int[] appWidgetIds) {
|
||||||
|
defaultAppWidget(context, appWidgetIds);
|
||||||
|
final Intent updateIntent = new Intent(Constants.APP_WIDGET_UPDATE);
|
||||||
|
updateIntent.putExtra(Constants.EXTRA_APP_WIDGET_NAME, NAME);
|
||||||
|
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
|
||||||
|
updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
|
||||||
|
context.sendBroadcast(updateIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a change notification coming over from {@link MusicService}
|
||||||
|
*/
|
||||||
|
public void notifyChange(final MusicService service, final String what) {
|
||||||
|
if (hasInstances(service)) {
|
||||||
|
if (Constants.META_CHANGED.equals(what) || Constants.PLAY_STATE_CHANGED.equals(what)) {
|
||||||
|
performUpdate(service, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void pushUpdate(final Context context, final int[] appWidgetIds,
|
||||||
|
final RemoteViews views) {
|
||||||
|
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||||
|
if (appWidgetIds != null) {
|
||||||
|
appWidgetManager.updateAppWidget(appWidgetIds, views);
|
||||||
|
} else {
|
||||||
|
appWidgetManager.updateAppWidget(new ComponentName(context, getClass()), views);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check against {@link AppWidgetManager} if there are any instances of this widget.
|
||||||
|
*/
|
||||||
|
protected boolean hasInstances(final Context context) {
|
||||||
|
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||||
|
final int[] mAppWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context,
|
||||||
|
getClass()));
|
||||||
|
return mAppWidgetIds.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PendingIntent buildPendingIntent(Context context, final String action,
|
||||||
|
final ComponentName serviceName) {
|
||||||
|
Intent intent = new Intent(action);
|
||||||
|
intent.setComponent(serviceName);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
return PendingIntent.getForegroundService(context, 0, intent, 0);
|
||||||
|
} else {
|
||||||
|
return PendingIntent.getService(context, 0, intent, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected void defaultAppWidget(final Context context, final int[] appWidgetIds);
|
||||||
|
|
||||||
|
abstract public void performUpdate(final MusicService service, final int[] appWidgetIds);
|
||||||
|
|
||||||
|
protected Drawable getAlbumArtDrawable(final Resources resources, final Bitmap bitmap) {
|
||||||
|
Drawable image;
|
||||||
|
if (bitmap == null) {
|
||||||
|
image = resources.getDrawable(R.drawable.default_album_art);
|
||||||
|
} else {
|
||||||
|
image = new BitmapDrawable(resources, bitmap);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getSongArtistAndAlbum(final Song song) {
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(song.artistName);
|
||||||
|
if (!TextUtils.isEmpty(song.artistName) && !TextUtils.isEmpty(song.albumName)) {
|
||||||
|
builder.append(" • ");
|
||||||
|
}
|
||||||
|
builder.append(song.albumName);
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.loaders.PlaylistLoader;
|
||||||
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.PlaylistsUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad)
|
||||||
|
*/
|
||||||
|
public class AddToPlaylistDialog extends RoundedBottomSheetDialogFragment implements AdapterView.OnItemClickListener {
|
||||||
|
|
||||||
|
@BindView(R.id.playlists)
|
||||||
|
ListView playlist;
|
||||||
|
@BindView(R.id.title)
|
||||||
|
TextView title;
|
||||||
|
List<Playlist> playlists;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static AddToPlaylistDialog create(Song song) {
|
||||||
|
ArrayList<Song> list = new ArrayList<>();
|
||||||
|
list.add(song);
|
||||||
|
return create(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static AddToPlaylistDialog create(ArrayList<Song> songs) {
|
||||||
|
AddToPlaylistDialog dialog = new AddToPlaylistDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelableArrayList("songs", songs);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.dialog_add_to_playlist, container, false);
|
||||||
|
ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
ArrayAdapter<String> playlistAdapter = new ArrayAdapter<>(getActivity(), R.layout.simple_list_item);
|
||||||
|
playlists = PlaylistLoader.getAllPlaylists(getActivity()).blockingFirst();
|
||||||
|
playlistAdapter.add(getActivity().getResources().getString(R.string.action_new_playlist));
|
||||||
|
|
||||||
|
for (int i = 1; i < playlists.size(); i++) {
|
||||||
|
playlistAdapter.add(playlists.get(i - 1).name);
|
||||||
|
playlistAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playlist.setAdapter(playlistAdapter);
|
||||||
|
this.playlist.setOnItemClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
|
//noinspection unchecked
|
||||||
|
final ArrayList<Song> songs = getArguments().getParcelableArrayList("songs");
|
||||||
|
|
||||||
|
if (songs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
dismiss();
|
||||||
|
CreatePlaylistDialog.create(songs)
|
||||||
|
.show(getActivity().getSupportFragmentManager(), "ADD_TO_PLAYLIST");
|
||||||
|
} else {
|
||||||
|
dismiss();
|
||||||
|
PlaylistsUtil.addToPlaylist(getActivity(), songs, playlists.get(i - 1).id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aidan Follestad (afollestad), modified by Karim Abou Zeid
|
||||||
|
*/
|
||||||
|
public class BlacklistFolderChooserDialog extends DialogFragment implements MaterialDialog.ListCallback {
|
||||||
|
|
||||||
|
private File parentFolder;
|
||||||
|
private File[] parentContents;
|
||||||
|
private boolean canGoUp = false;
|
||||||
|
|
||||||
|
private FolderCallback callback;
|
||||||
|
|
||||||
|
String initialPath = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||||
|
|
||||||
|
private String[] getContentsArray() {
|
||||||
|
if (parentContents == null) {
|
||||||
|
if (canGoUp) {
|
||||||
|
return new String[]{".."};
|
||||||
|
}
|
||||||
|
return new String[]{};
|
||||||
|
}
|
||||||
|
String[] results = new String[parentContents.length + (canGoUp ? 1 : 0)];
|
||||||
|
if (canGoUp) {
|
||||||
|
results[0] = "..";
|
||||||
|
}
|
||||||
|
for (int i = 0; i < parentContents.length; i++) {
|
||||||
|
results[canGoUp ? i + 1 : i] = parentContents[i].getName();
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File[] listFiles() {
|
||||||
|
File[] contents = parentFolder.listFiles();
|
||||||
|
List<File> results = new ArrayList<>();
|
||||||
|
if (contents != null) {
|
||||||
|
for (File fi : contents) {
|
||||||
|
if (fi.isDirectory()) {
|
||||||
|
results.add(fi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(results, new FolderSorter());
|
||||||
|
return results.toArray(new File[results.size()]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlacklistFolderChooserDialog create() {
|
||||||
|
return new BlacklistFolderChooserDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return new MaterialDialog.Builder(getActivity())
|
||||||
|
.title(R.string.md_error_label)
|
||||||
|
.content(R.string.md_storage_perm_error)
|
||||||
|
.positiveText(android.R.string.ok)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
savedInstanceState = new Bundle();
|
||||||
|
}
|
||||||
|
if (!savedInstanceState.containsKey("current_path")) {
|
||||||
|
savedInstanceState.putString("current_path", initialPath);
|
||||||
|
}
|
||||||
|
parentFolder = new File(savedInstanceState.getString("current_path", File.pathSeparator));
|
||||||
|
checkIfCanGoUp();
|
||||||
|
parentContents = listFiles();
|
||||||
|
MaterialDialog.Builder builder =
|
||||||
|
new MaterialDialog.Builder(getActivity())
|
||||||
|
.title(parentFolder.getAbsolutePath())
|
||||||
|
.items((CharSequence[]) getContentsArray())
|
||||||
|
.itemsCallback(this)
|
||||||
|
.autoDismiss(false)
|
||||||
|
.onPositive((dialog, which) -> {
|
||||||
|
dismiss();
|
||||||
|
callback.onFolderSelection(BlacklistFolderChooserDialog.this, parentFolder);
|
||||||
|
})
|
||||||
|
.onNegative((materialDialog, dialogAction) -> dismiss())
|
||||||
|
.positiveText(R.string.add_action)
|
||||||
|
.negativeText(android.R.string.cancel);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence s) {
|
||||||
|
if (canGoUp && i == 0) {
|
||||||
|
parentFolder = parentFolder.getParentFile();
|
||||||
|
if (parentFolder.getAbsolutePath().equals("/storage/emulated")) {
|
||||||
|
parentFolder = parentFolder.getParentFile();
|
||||||
|
}
|
||||||
|
checkIfCanGoUp();
|
||||||
|
} else {
|
||||||
|
parentFolder = parentContents[canGoUp ? i - 1 : i];
|
||||||
|
canGoUp = true;
|
||||||
|
if (parentFolder.getAbsolutePath().equals("/storage/emulated")) {
|
||||||
|
parentFolder = Environment.getExternalStorageDirectory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkIfCanGoUp() {
|
||||||
|
canGoUp = parentFolder.getParent() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reload() {
|
||||||
|
parentContents = listFiles();
|
||||||
|
MaterialDialog dialog = (MaterialDialog) getDialog();
|
||||||
|
dialog.setTitle(parentFolder.getAbsolutePath());
|
||||||
|
dialog.setItems((CharSequence[]) getContentsArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putString("current_path", parentFolder.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(FolderCallback callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface FolderCallback {
|
||||||
|
void onFolderSelection(@NonNull BlacklistFolderChooserDialog dialog, @NonNull File folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FolderSorter implements Comparator<File> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(File lhs, File rhs) {
|
||||||
|
return lhs.getName().compareTo(rhs.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.text.Html;
|
||||||
|
|
||||||
|
import com.afollestad.materialdialogs.DialogAction;
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClearSmartPlaylistDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ClearSmartPlaylistDialog create(AbsSmartPlaylist playlist) {
|
||||||
|
ClearSmartPlaylistDialog dialog = new ClearSmartPlaylistDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelable("playlist", playlist);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
//noinspection unchecked
|
||||||
|
final AbsSmartPlaylist playlist = getArguments().getParcelable("playlist");
|
||||||
|
int title = R.string.clear_playlist_title;
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
CharSequence content = Html.fromHtml(getString(R.string.clear_playlist_x, playlist.name));
|
||||||
|
|
||||||
|
return new MaterialDialog.Builder(getActivity())
|
||||||
|
.title(title)
|
||||||
|
.content(content)
|
||||||
|
.positiveText(R.string.clear_action)
|
||||||
|
.negativeText(android.R.string.cancel)
|
||||||
|
.onPositive(new MaterialDialog.SingleButtonCallback() {
|
||||||
|
@Override
|
||||||
|
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
|
||||||
|
if (getActivity() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
playlist.clear(getActivity());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.BottomSheetDialogFragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
import code.name.monkey.appthemehelper.util.TintHelper;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.PlaylistsUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad)
|
||||||
|
*/
|
||||||
|
public class CreatePlaylistDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
|
||||||
|
@BindView(R.id.option_1)
|
||||||
|
EditText playlistName;
|
||||||
|
@BindView(R.id.action_cancel)
|
||||||
|
Button actionCancel;
|
||||||
|
@BindView(R.id.action_create)
|
||||||
|
Button actionCreate;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static CreatePlaylistDialog create() {
|
||||||
|
return create((Song) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static CreatePlaylistDialog create(@Nullable Song song) {
|
||||||
|
ArrayList<Song> list = new ArrayList<>();
|
||||||
|
if (song != null) {
|
||||||
|
list.add(song);
|
||||||
|
}
|
||||||
|
return create(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static CreatePlaylistDialog create(ArrayList<Song> songs) {
|
||||||
|
CreatePlaylistDialog dialog = new CreatePlaylistDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelableArrayList("songs", songs);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.dialog_create_playlist, container, false);
|
||||||
|
ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
int accentColor = ThemeStore.accentColor(Objects.requireNonNull(getContext()));
|
||||||
|
TintHelper.setTintAuto(playlistName, accentColor, true);
|
||||||
|
TintHelper.setTintAuto(actionCreate, accentColor, true);
|
||||||
|
actionCancel.setTextColor(accentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({R.id.action_cancel, R.id.action_create})
|
||||||
|
void actions(View view) {
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.action_cancel:
|
||||||
|
dismiss();
|
||||||
|
break;
|
||||||
|
case R.id.action_create:
|
||||||
|
if (getActivity() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!playlistName.getText().toString().trim().isEmpty()) {
|
||||||
|
final int playlistId = PlaylistsUtil
|
||||||
|
.createPlaylist(getActivity(), playlistName.getText().toString());
|
||||||
|
if (playlistId != -1 && getActivity() != null) {
|
||||||
|
//noinspection unchecked
|
||||||
|
ArrayList<Song> songs = getArguments().getParcelableArrayList("songs");
|
||||||
|
if (songs != null) {
|
||||||
|
PlaylistsUtil.addToPlaylist(getActivity(), songs, playlistId, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
|
import code.name.monkey.retromusic.util.PlaylistsUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
|
||||||
|
public class DeletePlaylistDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
|
||||||
|
@BindView(R.id.action_delete)
|
||||||
|
TextView delete;
|
||||||
|
@BindView(R.id.title)
|
||||||
|
TextView title;
|
||||||
|
@BindView(R.id.action_cancel)
|
||||||
|
TextView cancel;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static DeletePlaylistDialog create(Playlist playlist) {
|
||||||
|
ArrayList<Playlist> list = new ArrayList<>();
|
||||||
|
list.add(playlist);
|
||||||
|
return create(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static DeletePlaylistDialog create(ArrayList<Playlist> playlists) {
|
||||||
|
DeletePlaylistDialog dialog = new DeletePlaylistDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelableArrayList("playlists", playlists);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.dialog_delete_playlist, container, false);
|
||||||
|
ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
//noinspection unchecked
|
||||||
|
final ArrayList<Playlist> playlists = getArguments().getParcelableArrayList("playlists");
|
||||||
|
int title;
|
||||||
|
CharSequence content;
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
if (playlists.size() > 1) {
|
||||||
|
title = R.string.delete_playlists_title;
|
||||||
|
content = Html.fromHtml(getString(R.string.delete_x_playlists, playlists.size()));
|
||||||
|
} else {
|
||||||
|
title = R.string.delete_playlist_title;
|
||||||
|
content = Html.fromHtml(getString(R.string.delete_playlist_x, playlists.get(0).name));
|
||||||
|
}
|
||||||
|
this.title.setText(title);
|
||||||
|
this.delete.setText(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({R.id.action_cancel, R.id.action_delete})
|
||||||
|
void actions(View view) {
|
||||||
|
final ArrayList<Playlist> playlists = getArguments().getParcelableArrayList("playlists");
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.action_delete:
|
||||||
|
if (getActivity() == null)
|
||||||
|
return;
|
||||||
|
PlaylistsUtil.deletePlaylists(getActivity(), playlists);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.text.Html;
|
||||||
|
|
||||||
|
import com.afollestad.materialdialogs.DialogAction;
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.BottomSheetDialogFragment;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class DeleteSongsDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
@BindView(R.id.action_delete)
|
||||||
|
TextView delete;
|
||||||
|
@BindView(R.id.title)
|
||||||
|
TextView title;
|
||||||
|
@BindView(R.id.action_cancel)
|
||||||
|
TextView cancel;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static DeleteSongsDialog create(Song song) {
|
||||||
|
ArrayList<Song> list = new ArrayList<>();
|
||||||
|
list.add(song);
|
||||||
|
return create(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static DeleteSongsDialog create(ArrayList<Song> songs) {
|
||||||
|
DeleteSongsDialog dialog = new DeleteSongsDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelableArrayList("songs", songs);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({R.id.action_cancel, R.id.action_delete})
|
||||||
|
void actions(View view) {
|
||||||
|
final ArrayList<Song> songs = getArguments().getParcelableArrayList("songs");
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.action_delete:
|
||||||
|
if (getActivity() == null)
|
||||||
|
return;
|
||||||
|
MusicUtil.deleteTracks(getActivity(), songs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
//noinspection unchecked
|
||||||
|
final ArrayList<Song> songs = getArguments().getParcelableArrayList("songs");
|
||||||
|
int title;
|
||||||
|
CharSequence content;
|
||||||
|
if (songs != null && songs.size() > 1) {
|
||||||
|
title = R.string.delete_songs_title;
|
||||||
|
content = Html.fromHtml(getString(R.string.delete_x_songs, songs.size()));
|
||||||
|
} else {
|
||||||
|
title = R.string.delete_song_title;
|
||||||
|
content = Html.fromHtml(getString(R.string.delete_song_x, songs.get(0).title));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.title.setText(title);
|
||||||
|
this.delete.setText(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.dialog_delete_songs, container, false);
|
||||||
|
ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v7.widget.AppCompatTextView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import butterknife.Unbinder;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.util.Compressor;
|
||||||
|
import code.name.monkey.retromusic.util.NavigationUtil;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import code.name.monkey.retromusic.views.CircularImageView;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
import static code.name.monkey.retromusic.Constants.USER_PROFILE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Hemanth S (h4h13).
|
||||||
|
*/
|
||||||
|
public class HomeOptionDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
private static final String TAG = "HomeOptionDialog";
|
||||||
|
Unbinder mUnbinder;
|
||||||
|
@BindView(R.id.user_image_bottom)
|
||||||
|
CircularImageView userImageBottom;
|
||||||
|
@BindView(R.id.title_welcome)
|
||||||
|
AppCompatTextView titleWelcome;
|
||||||
|
private CompositeDisposable disposable = new CompositeDisposable();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.user_action_details, container, false);
|
||||||
|
mUnbinder = ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
loadImageFromStorage();
|
||||||
|
titleWelcome.setText(String.format("%s, %s!", getTimeOfTheDay(), PreferenceUtil.getInstance(getContext()).getUserName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTimeOfTheDay() {
|
||||||
|
String message = getString(R.string.title_good_day);
|
||||||
|
Calendar c = Calendar.getInstance();
|
||||||
|
int timeOfDay = c.get(Calendar.HOUR_OF_DAY);
|
||||||
|
|
||||||
|
if (timeOfDay >= 0 && timeOfDay < 6) {
|
||||||
|
message = getString(R.string.title_good_night);
|
||||||
|
} else if (timeOfDay >= 6 && timeOfDay < 12) {
|
||||||
|
message = getString(R.string.title_good_morning);
|
||||||
|
} else if (timeOfDay >= 12 && timeOfDay < 16) {
|
||||||
|
message = getString(R.string.title_good_afternoon);
|
||||||
|
} else if (timeOfDay >= 16 && timeOfDay < 20) {
|
||||||
|
message = getString(R.string.title_good_evening);
|
||||||
|
} else if (timeOfDay >= 20 && timeOfDay < 24) {
|
||||||
|
message = getString(R.string.title_good_night);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
disposable.clear();
|
||||||
|
mUnbinder.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({R.id.action_about, R.id.user_info_container, R.id.action_folder, R.id.action_settings, R.id.action_sleep_timer})
|
||||||
|
public void onViewClicked(View view) {
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.user_info_container:
|
||||||
|
NavigationUtil.goToUserInfo(getActivity());
|
||||||
|
break;
|
||||||
|
case R.id.action_folder:
|
||||||
|
//getMainActivity().setCurrentFragment(FoldersFragment.newInstance(getContext()), true);
|
||||||
|
break;
|
||||||
|
case R.id.action_settings:
|
||||||
|
NavigationUtil.goToSettings(getActivity());
|
||||||
|
break;
|
||||||
|
case R.id.action_about:
|
||||||
|
NavigationUtil.goToAbout(getActivity());
|
||||||
|
break;
|
||||||
|
case R.id.action_sleep_timer:
|
||||||
|
if (getFragmentManager() != null) {
|
||||||
|
new SleepTimerDialog().show(getFragmentManager(), TAG);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadImageFromStorage() {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
disposable.add(new Compressor(getContext())
|
||||||
|
.setMaxHeight(300)
|
||||||
|
.setMaxWidth(300)
|
||||||
|
.setQuality(75)
|
||||||
|
.setCompressFormat(Bitmap.CompressFormat.WEBP)
|
||||||
|
.compressToBitmapAsFlowable(
|
||||||
|
new File(PreferenceUtil.getInstance(getContext()).getProfileImage(), USER_PROFILE))
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(userImageBottom::setImageBitmap,
|
||||||
|
throwable -> userImageBottom.setImageDrawable(ContextCompat
|
||||||
|
.getDrawable(getContext(), R.drawable.ic_person_flat))));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.BottomSheetDialogFragment;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.PlaylistSong;
|
||||||
|
import code.name.monkey.retromusic.util.PlaylistsUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
|
||||||
|
public class RemoveFromPlaylistDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
@BindView(R.id.action_remove)
|
||||||
|
TextView remove;
|
||||||
|
@BindView(R.id.title)
|
||||||
|
TextView title;
|
||||||
|
@BindView(R.id.action_cancel)
|
||||||
|
TextView cancel;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static RemoveFromPlaylistDialog create(PlaylistSong song) {
|
||||||
|
ArrayList<PlaylistSong> list = new ArrayList<>();
|
||||||
|
list.add(song);
|
||||||
|
return create(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static RemoveFromPlaylistDialog create(ArrayList<PlaylistSong> songs) {
|
||||||
|
RemoveFromPlaylistDialog dialog = new RemoveFromPlaylistDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelableArrayList("songs", songs);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({R.id.action_cancel, R.id.action_remove})
|
||||||
|
void actions(View view) {
|
||||||
|
final ArrayList<PlaylistSong> songs = getArguments().getParcelableArrayList("songs");
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.action_remove:
|
||||||
|
if (getActivity() == null)
|
||||||
|
return;
|
||||||
|
PlaylistsUtil.removeFromPlaylist(getActivity(), songs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.dialog_remove_from_playlist, container, false);
|
||||||
|
ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
//noinspection unchecked
|
||||||
|
final ArrayList<PlaylistSong> songs = getArguments().getParcelableArrayList("songs");
|
||||||
|
int title;
|
||||||
|
CharSequence content;
|
||||||
|
if (songs.size() > 1) {
|
||||||
|
title = R.string.remove_songs_from_playlist_title;
|
||||||
|
content = Html.fromHtml(getString(R.string.remove_x_songs_from_playlist, songs.size()));
|
||||||
|
} else {
|
||||||
|
title = R.string.remove_song_from_playlist_title;
|
||||||
|
content = Html.fromHtml(getString(R.string.remove_song_x_from_playlist, songs.get(0).title));
|
||||||
|
}
|
||||||
|
this.remove.setText(content);
|
||||||
|
this.title.setText(title);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.BottomSheetDialogFragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
import code.name.monkey.appthemehelper.util.TintHelper;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.util.PlaylistsUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad)
|
||||||
|
*/
|
||||||
|
public class RenamePlaylistDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
@BindView(R.id.option_1)
|
||||||
|
EditText playlistName;
|
||||||
|
@BindView(R.id.action_cancel)
|
||||||
|
Button cancel;
|
||||||
|
@BindView(R.id.action_rename)
|
||||||
|
Button rename;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static RenamePlaylistDialog create(long playlistId) {
|
||||||
|
RenamePlaylistDialog dialog = new RenamePlaylistDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putLong("playlist_id", playlistId);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({R.id.action_cancel, R.id.action_rename})
|
||||||
|
void actions(View view) {
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.action_cancel:
|
||||||
|
dismiss();
|
||||||
|
break;
|
||||||
|
case R.id.action_rename:
|
||||||
|
if (!playlistName.toString().trim().equals("")) {
|
||||||
|
long playlistId = getArguments().getLong("playlist_id");
|
||||||
|
PlaylistsUtil.renamePlaylist(getActivity(), playlistId, playlistName.toString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.dialog_playlist_rename, container, false);
|
||||||
|
ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
int accentColor = ThemeStore.accentColor(Objects.requireNonNull(getContext()));
|
||||||
|
TintHelper.setTintAuto(playlistName, accentColor, true);
|
||||||
|
TintHelper.setTintAuto(rename, accentColor, true);
|
||||||
|
cancel.setTextColor(accentColor);
|
||||||
|
|
||||||
|
long playlistId = getArguments().getLong("playlist_id");
|
||||||
|
playlistName.setText(PlaylistsUtil.getNameForPlaylist(getActivity(), playlistId));
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
173
app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java
Executable file
173
app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java
Executable file
|
@ -0,0 +1,173 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.CountDownTimer;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
import code.name.monkey.appthemehelper.util.TintHelper;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
import static code.name.monkey.retromusic.Constants.ACTION_QUIT;
|
||||||
|
|
||||||
|
public class SleepTimerDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
@BindView(R.id.seek_arc)
|
||||||
|
SeekBar seekArc;
|
||||||
|
@BindView(R.id.timer_display)
|
||||||
|
TextView timerDisplay;
|
||||||
|
@BindView(R.id.action_set)
|
||||||
|
Button setButton;
|
||||||
|
@BindView(R.id.action_cancel)
|
||||||
|
Button cancelButton;
|
||||||
|
|
||||||
|
private int seekArcProgress;
|
||||||
|
private TimerUpdater timerUpdater;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDismiss(DialogInterface dialog) {
|
||||||
|
super.onDismiss(dialog);
|
||||||
|
timerUpdater.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE) != null) {
|
||||||
|
timerUpdater.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.dialog_sleep_timer, container, false);
|
||||||
|
ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
timerUpdater = new TimerUpdater();
|
||||||
|
|
||||||
|
seekArcProgress = PreferenceUtil.getInstance(getActivity()).getLastSleepTimerValue();
|
||||||
|
updateTimeDisplayTime();
|
||||||
|
seekArc.setProgress(seekArcProgress);
|
||||||
|
|
||||||
|
int accentColor = ThemeStore.accentColor(getContext());
|
||||||
|
TintHelper.setTintAuto(seekArc, accentColor, true);
|
||||||
|
setButton.setTextColor(accentColor);
|
||||||
|
cancelButton.setTextColor(accentColor);
|
||||||
|
|
||||||
|
seekArc.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
|
||||||
|
if (i < 1) {
|
||||||
|
seekArc.setProgress(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seekArcProgress = i;
|
||||||
|
updateTimeDisplayTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
PreferenceUtil.getInstance(getActivity()).setLastSleepTimerValue(seekArcProgress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({R.id.action_cancel, R.id.action_set})
|
||||||
|
void set(View view) {
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.action_cancel:
|
||||||
|
if (getActivity() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final PendingIntent previous = makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE);
|
||||||
|
if (previous != null) {
|
||||||
|
AlarmManager am = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
|
||||||
|
if (am != null) {
|
||||||
|
am.cancel(previous);
|
||||||
|
}
|
||||||
|
previous.cancel();
|
||||||
|
Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.sleep_timer_canceled), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R.id.action_set:
|
||||||
|
if (getActivity() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final int minutes = seekArcProgress;
|
||||||
|
PendingIntent pi = makeTimerPendingIntent(PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
final long nextSleepTimerElapsedTime = SystemClock.elapsedRealtime() + minutes * 60 * 1000;
|
||||||
|
PreferenceUtil.getInstance(getActivity()).setNextSleepTimerElapsedRealtime(nextSleepTimerElapsedTime);
|
||||||
|
AlarmManager am = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
|
||||||
|
if (am != null) {
|
||||||
|
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextSleepTimerElapsedTime, pi);
|
||||||
|
}
|
||||||
|
Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.sleep_timer_set, minutes), Toast.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTimeDisplayTime() {
|
||||||
|
timerDisplay.setText(String.format(Locale.getDefault(), "%d min", seekArcProgress));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingIntent makeTimerPendingIntent(int flag) {
|
||||||
|
return PendingIntent.getService(getActivity(), 0, makeTimerIntent(), flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent makeTimerIntent() {
|
||||||
|
return new Intent(getActivity(), MusicService.class)
|
||||||
|
.setAction(ACTION_QUIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TimerUpdater extends CountDownTimer {
|
||||||
|
TimerUpdater() {
|
||||||
|
super(PreferenceUtil.getInstance(getActivity()).getNextSleepTimerElapsedRealTime() - SystemClock.elapsedRealtime(), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTick(long millisUntilFinished) {
|
||||||
|
cancelButton.setText(String.format("%s (%s)", getString(R.string.cancel_current_timer), MusicUtil.getReadableDurationString(millisUntilFinished)));
|
||||||
|
//materialDialog.setActionButton(DialogAction.NEUTRAL, materialDialog.getContext().getString(R.string.cancel_current_timer) + " (" + MusicUtil.getReadableDurationString(millisUntilFinished) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinish() {
|
||||||
|
cancelButton.setText(null);
|
||||||
|
//materialDialog.setActionButton(DialogAction.NEUTRAL, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.BottomSheetDialogFragment;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.jaudiotagger.audio.AudioFile;
|
||||||
|
import org.jaudiotagger.audio.AudioFileIO;
|
||||||
|
import org.jaudiotagger.audio.AudioHeader;
|
||||||
|
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||||
|
import org.jaudiotagger.tag.TagException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad)
|
||||||
|
*/
|
||||||
|
public class SongDetailDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
|
||||||
|
public static final String TAG = SongDetailDialog.class.getSimpleName();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static SongDetailDialog create(Song song) {
|
||||||
|
SongDetailDialog dialog = new SongDetailDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelable("song", song);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Spanned makeTextWithTitle(@NonNull Context context, int titleResId, String text) {
|
||||||
|
return Html.fromHtml("<b>" + context.getResources().getString(titleResId) + ": " + "</b>" + text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getFileSizeString(long sizeInBytes) {
|
||||||
|
long fileSizeInKB = sizeInBytes / 1024;
|
||||||
|
long fileSizeInMB = fileSizeInKB / 1024;
|
||||||
|
return fileSizeInMB + " MB";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View dialogView = inflater.inflate(R.layout.dialog_file_details, container, false);
|
||||||
|
Context context = getContext();
|
||||||
|
|
||||||
|
final TextView fileName = dialogView.findViewById(R.id.file_name);
|
||||||
|
final TextView filePath = dialogView.findViewById(R.id.file_path);
|
||||||
|
final TextView fileSize = dialogView.findViewById(R.id.file_size);
|
||||||
|
final TextView fileFormat = dialogView.findViewById(R.id.file_format);
|
||||||
|
final TextView trackLength = dialogView.findViewById(R.id.track_length);
|
||||||
|
final TextView bitRate = dialogView.findViewById(R.id.bitrate);
|
||||||
|
final TextView samplingRate = dialogView.findViewById(R.id.sampling_rate);
|
||||||
|
|
||||||
|
fileName.setText(makeTextWithTitle(context, R.string.label_file_name, "-"));
|
||||||
|
filePath.setText(makeTextWithTitle(context, R.string.label_file_path, "-"));
|
||||||
|
fileSize.setText(makeTextWithTitle(context, R.string.label_file_size, "-"));
|
||||||
|
fileFormat.setText(makeTextWithTitle(context, R.string.label_file_format, "-"));
|
||||||
|
trackLength.setText(makeTextWithTitle(context, R.string.label_track_length, "-"));
|
||||||
|
bitRate.setText(makeTextWithTitle(context, R.string.label_bit_rate, "-"));
|
||||||
|
samplingRate.setText(makeTextWithTitle(context, R.string.label_sampling_rate, "-"));
|
||||||
|
|
||||||
|
final Song song = getArguments().getParcelable("song");
|
||||||
|
if (song != null) {
|
||||||
|
final File songFile = new File(song.data);
|
||||||
|
if (songFile.exists()) {
|
||||||
|
fileName.setText(makeTextWithTitle(context, R.string.label_file_name, songFile.getName()));
|
||||||
|
filePath.setText(
|
||||||
|
makeTextWithTitle(context, R.string.label_file_path, songFile.getAbsolutePath()));
|
||||||
|
fileSize.setText(makeTextWithTitle(context, R.string.label_file_size,
|
||||||
|
getFileSizeString(songFile.length())));
|
||||||
|
try {
|
||||||
|
AudioFile audioFile = AudioFileIO.read(songFile);
|
||||||
|
AudioHeader audioHeader = audioFile.getAudioHeader();
|
||||||
|
|
||||||
|
fileFormat.setText(
|
||||||
|
makeTextWithTitle(context, R.string.label_file_format, audioHeader.getFormat()));
|
||||||
|
trackLength.setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil
|
||||||
|
.getReadableDurationString(audioHeader.getTrackLength() * 1000)));
|
||||||
|
bitRate.setText(makeTextWithTitle(context, R.string.label_bit_rate,
|
||||||
|
audioHeader.getBitRate() + " kb/s"));
|
||||||
|
samplingRate.setText(makeTextWithTitle(context, R.string.label_sampling_rate,
|
||||||
|
audioHeader.getSampleRate() + " Hz"));
|
||||||
|
} catch (@NonNull CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||||
|
Log.e(TAG, "error while reading the song file", e);
|
||||||
|
// fallback
|
||||||
|
trackLength.setText(makeTextWithTitle(context, R.string.label_track_length,
|
||||||
|
MusicUtil.getReadableDurationString(song.duration)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fallback
|
||||||
|
fileName.setText(makeTextWithTitle(context, R.string.label_file_name, song.title));
|
||||||
|
trackLength.setText(makeTextWithTitle(context, R.string.label_track_length,
|
||||||
|
MusicUtil.getReadableDurationString(song.duration)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialogView;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package code.name.monkey.retromusic.dialogs;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.BottomSheetDialogFragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment;
|
||||||
|
|
||||||
|
|
||||||
|
public class SongShareDialog extends RoundedBottomSheetDialogFragment {
|
||||||
|
@BindView(R.id.option_2)
|
||||||
|
TextView currentSong;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static SongShareDialog create(final Song song) {
|
||||||
|
final SongShareDialog dialog = new SongShareDialog();
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putParcelable("song", song);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View layout = inflater.inflate(R.layout.dialog_file_share, container, false);
|
||||||
|
ButterKnife.bind(this, layout);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("StringFormatInvalid")
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
final Song song = getArguments().getParcelable("song");
|
||||||
|
currentSong.setText(getString(R.string.currently_listening_to_x_by_x, song.title, song.artistName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({R.id.option_2, R.id.option_1})
|
||||||
|
void onClick(View view) {
|
||||||
|
final Song song = getArguments().getParcelable("song");
|
||||||
|
final String currentlyListening = getString(R.string.currently_listening_to_x_by_x, song.title, song.artistName);
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.option_1:
|
||||||
|
startActivity(
|
||||||
|
Intent.createChooser(
|
||||||
|
MusicUtil.createShareSongFileIntent(song, getContext()), null));
|
||||||
|
break;
|
||||||
|
case R.id.option_2:
|
||||||
|
getActivity().startActivity(Intent.createChooser(
|
||||||
|
new Intent().setAction(Intent.ACTION_SEND)
|
||||||
|
.putExtra(Intent.EXTRA_TEXT, currentlyListening)
|
||||||
|
.setType("text/plain"), null));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
package code.name.monkey.retromusic.glide;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.bumptech.glide.BitmapRequestBuilder;
|
||||||
|
import com.bumptech.glide.DrawableRequestBuilder;
|
||||||
|
import com.bumptech.glide.DrawableTypeRequest;
|
||||||
|
import com.bumptech.glide.Priority;
|
||||||
|
import com.bumptech.glide.RequestManager;
|
||||||
|
import com.bumptech.glide.load.Key;
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||||
|
import com.bumptech.glide.request.target.Target;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.RetroApplication;
|
||||||
|
import code.name.monkey.retromusic.glide.artistimage.ArtistImage;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||||
|
import code.name.monkey.retromusic.model.Artist;
|
||||||
|
import code.name.monkey.retromusic.util.ArtistSignatureUtil;
|
||||||
|
import code.name.monkey.retromusic.util.CustomArtistImageUtil;
|
||||||
|
|
||||||
|
public class ArtistGlideRequest {
|
||||||
|
|
||||||
|
private static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||||
|
private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.SOURCE;
|
||||||
|
private static final int DEFAULT_ERROR_IMAGE = R.drawable.default_artist_art;
|
||||||
|
|
||||||
|
private static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Artist artist,
|
||||||
|
boolean noCustomImage, boolean forceDownload) {
|
||||||
|
boolean hasCustomImage = CustomArtistImageUtil.getInstance(RetroApplication.getInstance())
|
||||||
|
.hasCustomArtistImage(artist);
|
||||||
|
if (noCustomImage || !hasCustomImage) {
|
||||||
|
return requestManager.load(new ArtistImage(artist.getName(), forceDownload));
|
||||||
|
} else {
|
||||||
|
return requestManager.load(CustomArtistImageUtil.getFile(artist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Key createSignature(Artist artist) {
|
||||||
|
return ArtistSignatureUtil.getInstance(RetroApplication.getInstance())
|
||||||
|
.getArtistSignature(artist.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
final RequestManager requestManager;
|
||||||
|
final Artist artist;
|
||||||
|
boolean noCustomImage;
|
||||||
|
boolean forceDownload;
|
||||||
|
|
||||||
|
private Builder(@NonNull RequestManager requestManager, Artist artist) {
|
||||||
|
this.requestManager = requestManager;
|
||||||
|
this.artist = artist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder from(@NonNull RequestManager requestManager, Artist artist) {
|
||||||
|
return new Builder(requestManager, artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaletteBuilder generatePalette(Context context) {
|
||||||
|
return new PaletteBuilder(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapBuilder asBitmap() {
|
||||||
|
return new BitmapBuilder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder noCustomImage(boolean noCustomImage) {
|
||||||
|
this.noCustomImage = noCustomImage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder forceDownload(boolean forceDownload) {
|
||||||
|
this.forceDownload = forceDownload;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableRequestBuilder<GlideDrawable> build() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return createBaseRequest(requestManager, artist, noCustomImage, forceDownload)
|
||||||
|
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||||
|
.error(DEFAULT_ERROR_IMAGE)
|
||||||
|
.animate(DEFAULT_ANIMATION)
|
||||||
|
.priority(Priority.LOW)
|
||||||
|
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||||
|
.signature(createSignature(artist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BitmapBuilder {
|
||||||
|
|
||||||
|
private final Builder builder;
|
||||||
|
|
||||||
|
BitmapBuilder(Builder builder) {
|
||||||
|
this.builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapRequestBuilder<?, Bitmap> build() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage,
|
||||||
|
builder.forceDownload)
|
||||||
|
.asBitmap()
|
||||||
|
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||||
|
.error(DEFAULT_ERROR_IMAGE)
|
||||||
|
.animate(DEFAULT_ANIMATION)
|
||||||
|
.priority(Priority.LOW)
|
||||||
|
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||||
|
.signature(createSignature(builder.artist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PaletteBuilder {
|
||||||
|
|
||||||
|
final Context context;
|
||||||
|
private final Builder builder;
|
||||||
|
|
||||||
|
PaletteBuilder(Builder builder, Context context) {
|
||||||
|
this.builder = builder;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapRequestBuilder<?, BitmapPaletteWrapper> build() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage,
|
||||||
|
builder.forceDownload)
|
||||||
|
.asBitmap()
|
||||||
|
.transcode(new BitmapPaletteTranscoder(context), BitmapPaletteWrapper.class)
|
||||||
|
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||||
|
.error(DEFAULT_ERROR_IMAGE)
|
||||||
|
.animate(DEFAULT_ANIMATION)
|
||||||
|
.priority(Priority.LOW)
|
||||||
|
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||||
|
.signature(createSignature(builder.artist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package code.name.monkey.retromusic.glide;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.renderscript.Allocation;
|
||||||
|
import android.renderscript.Element;
|
||||||
|
import android.renderscript.RSRuntimeException;
|
||||||
|
import android.renderscript.RenderScript;
|
||||||
|
import android.renderscript.ScriptIntrinsicBlur;
|
||||||
|
import android.support.annotation.FloatRange;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
||||||
|
import code.name.monkey.retromusic.helper.StackBlur;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.BuildConfig;
|
||||||
|
import code.name.monkey.retromusic.util.ImageUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class BlurTransformation extends BitmapTransformation {
|
||||||
|
static final float DEFAULT_BLUR_RADIUS = 5f;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private float blurRadius;
|
||||||
|
private int sampling;
|
||||||
|
|
||||||
|
private BlurTransformation(Builder builder) {
|
||||||
|
super(builder.context);
|
||||||
|
init(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlurTransformation(Builder builder, BitmapPool bitmapPool) {
|
||||||
|
super(bitmapPool);
|
||||||
|
init(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Builder builder) {
|
||||||
|
this.context = builder.context;
|
||||||
|
this.blurRadius = builder.blurRadius;
|
||||||
|
this.sampling = builder.sampling;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
|
||||||
|
int sampling;
|
||||||
|
if (this.sampling == 0) {
|
||||||
|
sampling = ImageUtil.calculateInSampleSize(toTransform.getWidth(), toTransform.getHeight(), 100);
|
||||||
|
} else {
|
||||||
|
sampling = this.sampling;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = toTransform.getWidth();
|
||||||
|
int height = toTransform.getHeight();
|
||||||
|
int scaledWidth = width / sampling;
|
||||||
|
int scaledHeight = height / sampling;
|
||||||
|
|
||||||
|
Bitmap out = pool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
|
||||||
|
if (out == null) {
|
||||||
|
out = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas canvas = new Canvas(out);
|
||||||
|
canvas.scale(1 / (float) sampling, 1 / (float) sampling);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
|
||||||
|
canvas.drawBitmap(toTransform, 0, 0, paint);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 17) {
|
||||||
|
try {
|
||||||
|
final RenderScript rs = RenderScript.create(context.getApplicationContext());
|
||||||
|
final Allocation input = Allocation.createFromBitmap(rs, out, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
|
||||||
|
final Allocation output = Allocation.createTyped(rs, input.getType());
|
||||||
|
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
|
||||||
|
|
||||||
|
script.setRadius(blurRadius);
|
||||||
|
script.setInput(input);
|
||||||
|
script.forEach(output);
|
||||||
|
|
||||||
|
output.copyTo(out);
|
||||||
|
|
||||||
|
rs.destroy();
|
||||||
|
|
||||||
|
return out;
|
||||||
|
|
||||||
|
} catch (RSRuntimeException e) {
|
||||||
|
// on some devices RenderScript.create() throws: android.support.v8.renderscript.RSRuntimeException: Error loading libRSSupport library
|
||||||
|
if (BuildConfig.DEBUG) e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return StackBlur.blur(out, blurRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "BlurTransformation(radius=" + blurRadius + ", sampling=" + sampling + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private Context context;
|
||||||
|
private BitmapPool bitmapPool;
|
||||||
|
private float blurRadius = DEFAULT_BLUR_RADIUS;
|
||||||
|
private int sampling;
|
||||||
|
|
||||||
|
public Builder(@NonNull Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param blurRadius The radius to use. Must be between 0 and 25. Default is 5.
|
||||||
|
* @return the same Builder
|
||||||
|
*/
|
||||||
|
public Builder blurRadius(@FloatRange(from = 0.0f, to = 25.0f) float blurRadius) {
|
||||||
|
this.blurRadius = blurRadius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param sampling The inSampleSize to use. Must be a power of 2, or 1 for no down sampling or 0 for auto detect sampling. Default is 0.
|
||||||
|
* @return the same Builder
|
||||||
|
*/
|
||||||
|
public Builder sampling(int sampling) {
|
||||||
|
this.sampling = sampling;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bitmapPool The BitmapPool to use.
|
||||||
|
* @return the same Builder
|
||||||
|
*/
|
||||||
|
public Builder bitmapPool(BitmapPool bitmapPool) {
|
||||||
|
this.bitmapPool = bitmapPool;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlurTransformation build() {
|
||||||
|
if (bitmapPool != null) {
|
||||||
|
return new BlurTransformation(this, bitmapPool);
|
||||||
|
}
|
||||||
|
return new BlurTransformation(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package code.name.monkey.retromusic.glide;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
|
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.RetroApplication;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
|
||||||
|
import static code.name.monkey.retromusic.util.RetroColorUtil.getColor;
|
||||||
|
import static code.name.monkey.retromusic.util.RetroColorUtil.getDominantColor;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class RetroMusicColoredTarget extends BitmapPaletteTarget {
|
||||||
|
|
||||||
|
public RetroMusicColoredTarget(ImageView view) {
|
||||||
|
super(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFailed(Exception e, Drawable errorDrawable) {
|
||||||
|
super.onLoadFailed(e, errorDrawable);
|
||||||
|
onColorReady(getDefaultFooterColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(BitmapPaletteWrapper resource,
|
||||||
|
GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
|
||||||
|
super.onResourceReady(resource, glideAnimation);
|
||||||
|
int defaultColor = getDefaultFooterColor();
|
||||||
|
|
||||||
|
int primaryColor = getColor(resource.getPalette(), defaultColor);
|
||||||
|
int dominantColor = getDominantColor(resource.getBitmap(), defaultColor);
|
||||||
|
|
||||||
|
onColorReady(PreferenceUtil.getInstance(RetroApplication.getInstance()).isDominantColor() ?
|
||||||
|
dominantColor : primaryColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getDefaultFooterColor() {
|
||||||
|
return ATHUtil.resolveColor(getView().getContext(), R.attr.defaultFooterColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getAlbumArtistFooterColor() {
|
||||||
|
return ATHUtil.resolveColor(getView().getContext(), R.attr.cardBackgroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onColorReady(int color);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package code.name.monkey.retromusic.glide;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.GlideBuilder;
|
||||||
|
import com.bumptech.glide.module.GlideModule;
|
||||||
|
import code.name.monkey.retromusic.glide.artistimage.ArtistImage;
|
||||||
|
import code.name.monkey.retromusic.glide.artistimage.ArtistImageLoader;
|
||||||
|
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover;
|
||||||
|
import code.name.monkey.retromusic.glide.audiocover.AudioFileCoverLoader;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class RetroMusicGlideModule implements GlideModule {
|
||||||
|
@Override
|
||||||
|
public void applyOptions(Context context, GlideBuilder builder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerComponents(Context context, Glide glide) {
|
||||||
|
glide.register(AudioFileCover.class, InputStream.class, new AudioFileCoverLoader.Factory());
|
||||||
|
glide.register(ArtistImage.class, InputStream.class, new ArtistImageLoader.Factory(context));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package code.name.monkey.retromusic.glide;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder;
|
||||||
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import com.bumptech.glide.BitmapRequestBuilder;
|
||||||
|
import com.bumptech.glide.DrawableRequestBuilder;
|
||||||
|
import com.bumptech.glide.DrawableTypeRequest;
|
||||||
|
import com.bumptech.glide.RequestManager;
|
||||||
|
import com.bumptech.glide.load.Key;
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||||
|
import com.bumptech.glide.signature.MediaStoreSignature;
|
||||||
|
|
||||||
|
|
||||||
|
public class SongGlideRequest {
|
||||||
|
|
||||||
|
static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE;
|
||||||
|
static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||||
|
static final int DEFAULT_ERROR_IMAGE = R.drawable.default_album_art;
|
||||||
|
|
||||||
|
static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Song song,
|
||||||
|
boolean ignoreMediaStore) {
|
||||||
|
if (ignoreMediaStore) {
|
||||||
|
return requestManager.load(new AudioFileCover(song.data));
|
||||||
|
} else {
|
||||||
|
return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Key createSignature(Song song) {
|
||||||
|
return new MediaStoreSignature("", song.dateModified, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
final RequestManager requestManager;
|
||||||
|
final Song song;
|
||||||
|
boolean ignoreMediaStore;
|
||||||
|
|
||||||
|
private Builder(@NonNull RequestManager requestManager, Song song) {
|
||||||
|
this.requestManager = requestManager;
|
||||||
|
this.song = song;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder from(@NonNull RequestManager requestManager, Song song) {
|
||||||
|
return new Builder(requestManager, song);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaletteBuilder generatePalette(Context context) {
|
||||||
|
return new PaletteBuilder(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapBuilder asBitmap() {
|
||||||
|
return new BitmapBuilder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder checkIgnoreMediaStore(Context context) {
|
||||||
|
return ignoreMediaStore(PreferenceUtil.getInstance(context).ignoreMediaStoreArtwork());
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder ignoreMediaStore(boolean ignoreMediaStore) {
|
||||||
|
this.ignoreMediaStore = ignoreMediaStore;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableRequestBuilder<GlideDrawable> build() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return createBaseRequest(requestManager, song, ignoreMediaStore)
|
||||||
|
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||||
|
.error(DEFAULT_ERROR_IMAGE)
|
||||||
|
.animate(DEFAULT_ANIMATION)
|
||||||
|
.signature(createSignature(song));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BitmapBuilder {
|
||||||
|
|
||||||
|
private final Builder builder;
|
||||||
|
|
||||||
|
BitmapBuilder(Builder builder) {
|
||||||
|
this.builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapRequestBuilder<?, Bitmap> build() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return createBaseRequest(builder.requestManager, builder.song, builder.ignoreMediaStore)
|
||||||
|
.asBitmap()
|
||||||
|
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||||
|
.error(DEFAULT_ERROR_IMAGE)
|
||||||
|
.animate(DEFAULT_ANIMATION)
|
||||||
|
.signature(createSignature(builder.song));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PaletteBuilder {
|
||||||
|
|
||||||
|
final Context context;
|
||||||
|
private final Builder builder;
|
||||||
|
|
||||||
|
PaletteBuilder(Builder builder, Context context) {
|
||||||
|
this.builder = builder;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapRequestBuilder<?, BitmapPaletteWrapper> build() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return createBaseRequest(builder.requestManager, builder.song, builder.ignoreMediaStore)
|
||||||
|
.asBitmap()
|
||||||
|
.transcode(new BitmapPaletteTranscoder(context), BitmapPaletteWrapper.class)
|
||||||
|
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||||
|
.error(DEFAULT_ERROR_IMAGE)
|
||||||
|
.animate(DEFAULT_ANIMATION)
|
||||||
|
.signature(createSignature(builder.song));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package code.name.monkey.retromusic.glide.artistimage;
|
||||||
|
|
||||||
|
|
||||||
|
public class ArtistImage {
|
||||||
|
public final String artistName;
|
||||||
|
public final boolean skipOkHttpCache;
|
||||||
|
|
||||||
|
public ArtistImage(String artistName, boolean skipOkHttpCache) {
|
||||||
|
this.artistName = artistName;
|
||||||
|
this.skipOkHttpCache = skipOkHttpCache;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package code.name.monkey.retromusic.glide.artistimage;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Priority;
|
||||||
|
import com.bumptech.glide.load.data.DataFetcher;
|
||||||
|
import com.bumptech.glide.load.model.GlideUrl;
|
||||||
|
import com.bumptech.glide.load.model.ModelLoader;
|
||||||
|
import code.name.monkey.retromusic.rest.LastFMRestClient;
|
||||||
|
import code.name.monkey.retromusic.rest.model.LastFmArtist;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.util.LastFMUtil;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
|
||||||
|
public class ArtistImageFetcher implements DataFetcher<InputStream> {
|
||||||
|
public static final String TAG = ArtistImageFetcher.class.getSimpleName();
|
||||||
|
private final LastFMRestClient lastFMRestClient;
|
||||||
|
private final ArtistImage model;
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
private Context context;
|
||||||
|
private ModelLoader<GlideUrl, InputStream> urlLoader;
|
||||||
|
private volatile boolean isCancelled;
|
||||||
|
private DataFetcher<InputStream> urlFetcher;
|
||||||
|
|
||||||
|
public ArtistImageFetcher(Context context, LastFMRestClient lastFMRestClient, ArtistImage model, ModelLoader<GlideUrl, InputStream> urlLoader, int width, int height) {
|
||||||
|
this.context = context;
|
||||||
|
this.lastFMRestClient = lastFMRestClient;
|
||||||
|
this.model = model;
|
||||||
|
this.urlLoader = urlLoader;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
// makes sure we never ever return null here
|
||||||
|
return String.valueOf(model.artistName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream loadData(Priority priority) throws Exception {
|
||||||
|
if (!MusicUtil.isArtistNameUnknown(model.artistName) && RetroUtil.isAllowedToDownloadMetadata(context)) {
|
||||||
|
Response<LastFmArtist> response = lastFMRestClient.getApiService().getArtistInfo(model.artistName, null, model.skipOkHttpCache ? "no-cache" : null).execute();
|
||||||
|
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
throw new IOException("Request failed with code: " + response.code());
|
||||||
|
}
|
||||||
|
|
||||||
|
LastFmArtist lastFmArtist = response.body();
|
||||||
|
|
||||||
|
if (isCancelled) return null;
|
||||||
|
|
||||||
|
GlideUrl url = new GlideUrl(LastFMUtil.getLargestArtistImageUrl(lastFmArtist.getArtist().getImage()));
|
||||||
|
urlFetcher = urlLoader.getResourceFetcher(url, width, height);
|
||||||
|
|
||||||
|
return urlFetcher.loadData(priority);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() {
|
||||||
|
if (urlFetcher != null) {
|
||||||
|
urlFetcher.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
isCancelled = true;
|
||||||
|
if (urlFetcher != null) {
|
||||||
|
urlFetcher.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package code.name.monkey.retromusic.glide.artistimage;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
|
||||||
|
import com.bumptech.glide.load.data.DataFetcher;
|
||||||
|
import com.bumptech.glide.load.model.GenericLoaderFactory;
|
||||||
|
import com.bumptech.glide.load.model.GlideUrl;
|
||||||
|
import com.bumptech.glide.load.model.ModelLoader;
|
||||||
|
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||||
|
import com.bumptech.glide.load.model.stream.StreamModelLoader;
|
||||||
|
import code.name.monkey.retromusic.rest.LastFMRestClient;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class ArtistImageLoader implements StreamModelLoader<ArtistImage> {
|
||||||
|
// we need these very low values to make sure our artist image loading calls doesn't block the image loading queue
|
||||||
|
private static final int TIMEOUT = 500;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private LastFMRestClient lastFMClient;
|
||||||
|
private ModelLoader<GlideUrl, InputStream> urlLoader;
|
||||||
|
|
||||||
|
public ArtistImageLoader(Context context, LastFMRestClient lastFMRestClient, ModelLoader<GlideUrl, InputStream> urlLoader) {
|
||||||
|
this.context = context;
|
||||||
|
this.lastFMClient = lastFMRestClient;
|
||||||
|
this.urlLoader = urlLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataFetcher<InputStream> getResourceFetcher(ArtistImage model, int width, int height) {
|
||||||
|
return new ArtistImageFetcher(context, lastFMClient, model, urlLoader, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements ModelLoaderFactory<ArtistImage, InputStream> {
|
||||||
|
private LastFMRestClient lastFMClient;
|
||||||
|
private OkHttpUrlLoader.Factory okHttpFactory;
|
||||||
|
|
||||||
|
public Factory(Context context) {
|
||||||
|
okHttpFactory = new OkHttpUrlLoader.Factory(new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.readTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.build());
|
||||||
|
lastFMClient = new LastFMRestClient(LastFMRestClient.createDefaultOkHttpClientBuilder(context)
|
||||||
|
.connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.readTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelLoader<ArtistImage, InputStream> build(Context context, GenericLoaderFactory factories) {
|
||||||
|
return new ArtistImageLoader(context, lastFMClient, okHttpFactory.build(context, factories));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void teardown() {
|
||||||
|
okHttpFactory.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package code.name.monkey.retromusic.glide.audiocover;
|
||||||
|
|
||||||
|
|
||||||
|
public class AudioFileCover {
|
||||||
|
public final String filePath;
|
||||||
|
|
||||||
|
public AudioFileCover(String filePath) {
|
||||||
|
this.filePath = filePath;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package code.name.monkey.retromusic.glide.audiocover;
|
||||||
|
|
||||||
|
import android.media.MediaMetadataRetriever;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Priority;
|
||||||
|
import com.bumptech.glide.load.data.DataFetcher;
|
||||||
|
|
||||||
|
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||||
|
import org.jaudiotagger.audio.mp3.MP3File;
|
||||||
|
import org.jaudiotagger.tag.TagException;
|
||||||
|
import org.jaudiotagger.tag.images.Artwork;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class AudioFileCoverFetcher implements DataFetcher<InputStream> {
|
||||||
|
private static final String[] FALLBACKS = {"cover.jpg", "album.jpg", "folder.jpg", "cover.png", "album.png", "folder.png"};
|
||||||
|
private final AudioFileCover model;
|
||||||
|
private FileInputStream stream;
|
||||||
|
|
||||||
|
public AudioFileCoverFetcher(AudioFileCover model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
// makes sure we never ever return null here
|
||||||
|
return String.valueOf(model.filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream loadData(Priority priority) throws Exception {
|
||||||
|
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||||
|
try {
|
||||||
|
retriever.setDataSource(model.filePath);
|
||||||
|
byte[] picture = retriever.getEmbeddedPicture();
|
||||||
|
if (picture != null) {
|
||||||
|
return new ByteArrayInputStream(picture);
|
||||||
|
} else {
|
||||||
|
return fallback(model.filePath);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
retriever.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream fallback(String path) throws FileNotFoundException {
|
||||||
|
// Method 1: use embedded high resolution album art if there is any
|
||||||
|
try {
|
||||||
|
MP3File mp3File = new MP3File(path);
|
||||||
|
if (mp3File.hasID3v2Tag()) {
|
||||||
|
Artwork art = mp3File.getTag().getFirstArtwork();
|
||||||
|
if (art != null) {
|
||||||
|
byte[] imageData = art.getBinaryData();
|
||||||
|
return new ByteArrayInputStream(imageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If there are any exceptions, we ignore them and continue to the other fallback method
|
||||||
|
} catch (ReadOnlyFileException | InvalidAudioFrameException | TagException | IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method 2: look for album art in external files
|
||||||
|
File parent = new File(path).getParentFile();
|
||||||
|
for (String fallback : FALLBACKS) {
|
||||||
|
File cover = new File(parent, fallback);
|
||||||
|
if (cover.exists()) {
|
||||||
|
return stream = new FileInputStream(cover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() {
|
||||||
|
// already cleaned up in loadData and ByteArrayInputStream will be GC'd
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
// can't do much about it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
// cannot cancel
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package code.name.monkey.retromusic.glide.audiocover;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.data.DataFetcher;
|
||||||
|
import com.bumptech.glide.load.model.GenericLoaderFactory;
|
||||||
|
import com.bumptech.glide.load.model.ModelLoader;
|
||||||
|
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||||
|
import com.bumptech.glide.load.model.stream.StreamModelLoader;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class AudioFileCoverLoader implements StreamModelLoader<AudioFileCover> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataFetcher<InputStream> getResourceFetcher(AudioFileCover model, int width, int height) {
|
||||||
|
return new AudioFileCoverFetcher(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements ModelLoaderFactory<AudioFileCover, InputStream> {
|
||||||
|
@Override
|
||||||
|
public ModelLoader<AudioFileCover, InputStream> build(Context context, GenericLoaderFactory factories) {
|
||||||
|
return new AudioFileCoverLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void teardown() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package code.name.monkey.retromusic.glide.palette;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.engine.Resource;
|
||||||
|
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||||
|
import com.bumptech.glide.util.Util;
|
||||||
|
|
||||||
|
|
||||||
|
public class BitmapPaletteResource implements Resource<BitmapPaletteWrapper> {
|
||||||
|
|
||||||
|
private final BitmapPaletteWrapper bitmapPaletteWrapper;
|
||||||
|
private final BitmapPool bitmapPool;
|
||||||
|
|
||||||
|
public BitmapPaletteResource(BitmapPaletteWrapper bitmapPaletteWrapper, BitmapPool bitmapPool) {
|
||||||
|
this.bitmapPaletteWrapper = bitmapPaletteWrapper;
|
||||||
|
this.bitmapPool = bitmapPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BitmapPaletteWrapper get() {
|
||||||
|
return bitmapPaletteWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return Util.getBitmapByteSize(bitmapPaletteWrapper.getBitmap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recycle() {
|
||||||
|
if (!bitmapPool.put(bitmapPaletteWrapper.getBitmap())) {
|
||||||
|
bitmapPaletteWrapper.getBitmap().recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package code.name.monkey.retromusic.glide.palette;
|
||||||
|
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import com.bumptech.glide.request.target.ImageViewTarget;
|
||||||
|
|
||||||
|
public class BitmapPaletteTarget extends ImageViewTarget<BitmapPaletteWrapper> {
|
||||||
|
|
||||||
|
public BitmapPaletteTarget(ImageView view) {
|
||||||
|
super(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setResource(BitmapPaletteWrapper bitmapPaletteWrapper) {
|
||||||
|
view.setImageBitmap(bitmapPaletteWrapper.getBitmap());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package code.name.monkey.retromusic.glide.palette;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.engine.Resource;
|
||||||
|
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||||
|
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
|
||||||
|
import code.name.monkey.retromusic.util.RetroColorUtil;
|
||||||
|
|
||||||
|
public class BitmapPaletteTranscoder implements ResourceTranscoder<Bitmap, BitmapPaletteWrapper> {
|
||||||
|
private final BitmapPool bitmapPool;
|
||||||
|
|
||||||
|
public BitmapPaletteTranscoder(Context context) {
|
||||||
|
this(Glide.get(context).getBitmapPool());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapPaletteTranscoder(BitmapPool bitmapPool) {
|
||||||
|
this.bitmapPool = bitmapPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resource<BitmapPaletteWrapper> transcode(Resource<Bitmap> bitmapResource) {
|
||||||
|
Bitmap bitmap = bitmapResource.get();
|
||||||
|
BitmapPaletteWrapper bitmapPaletteWrapper = new BitmapPaletteWrapper(bitmap, RetroColorUtil.generatePalette(bitmap));
|
||||||
|
return new BitmapPaletteResource(bitmapPaletteWrapper, bitmapPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "BitmapPaletteTranscoder.code.name.monkey.retromusic.glide.palette";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package code.name.monkey.retromusic.glide.palette;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.support.v7.graphics.Palette;
|
||||||
|
|
||||||
|
public class BitmapPaletteWrapper {
|
||||||
|
private final Bitmap mBitmap;
|
||||||
|
private final Palette mPalette;
|
||||||
|
|
||||||
|
public BitmapPaletteWrapper(Bitmap bitmap, Palette palette) {
|
||||||
|
mBitmap = bitmap;
|
||||||
|
mPalette = palette;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bitmap getBitmap() {
|
||||||
|
return mBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Palette getPalette() {
|
||||||
|
return mPalette;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.media.audiofx.BassBoost;
|
||||||
|
import android.media.audiofx.Equalizer;
|
||||||
|
import android.media.audiofx.Virtualizer;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.interfaces.EqualizerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Hemanth S (h4h13).
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class EqualizerHelper implements EqualizerInterface {
|
||||||
|
private static final String TAG = "EqualizerHelper";
|
||||||
|
private static volatile EqualizerHelper ourInstance;
|
||||||
|
private Equalizer mEqualizer;
|
||||||
|
private BassBoost mBassBoost;
|
||||||
|
private Virtualizer mVirtualizer;
|
||||||
|
|
||||||
|
private int mMaxLevel, mMinLevel;
|
||||||
|
private boolean isRunning = false;
|
||||||
|
|
||||||
|
private EqualizerHelper() {
|
||||||
|
|
||||||
|
//Prevent form the reflection api.
|
||||||
|
if (ourInstance != null) {
|
||||||
|
throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = MusicPlayerRemote.getAudioSessionId();
|
||||||
|
|
||||||
|
mEqualizer = new Equalizer(100, i);
|
||||||
|
if (mEqualizer == null) {
|
||||||
|
Log.i(TAG, "onCreate: Equalizer is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mEqualizer.setEnabled(true);
|
||||||
|
|
||||||
|
|
||||||
|
mBassBoost = new BassBoost(100, i);
|
||||||
|
if (mBassBoost == null) {
|
||||||
|
Log.i(TAG, "onCreate: BassBoost is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mVirtualizer = new Virtualizer(100, i);
|
||||||
|
if (mVirtualizer == null) {
|
||||||
|
Log.i(TAG, "onCreate: Virtualizer is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMaxLevel = (int) mEqualizer.getBandLevelRange()[1];
|
||||||
|
mMinLevel = (int) mEqualizer.getBandLevelRange()[0];
|
||||||
|
|
||||||
|
Log.i(TAG, "onCreate: " + mMaxLevel + " " + mMinLevel);
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EqualizerHelper getInstance() {
|
||||||
|
//Double check locking pattern
|
||||||
|
if (ourInstance == null) {//Check for the first time
|
||||||
|
|
||||||
|
synchronized (EqualizerHelper.class) {//Check for the second time.
|
||||||
|
|
||||||
|
//if there is no instance available... create new one
|
||||||
|
if (ourInstance == null) {
|
||||||
|
ourInstance = new EqualizerHelper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ourInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make singleton from serialize and deserialize operation.
|
||||||
|
protected EqualizerHelper readResolve() {
|
||||||
|
return getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Equalizer getEqualizer() {
|
||||||
|
return mEqualizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BassBoost getBassBoost() {
|
||||||
|
return mBassBoost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Virtualizer getVirtualizer() {
|
||||||
|
return mVirtualizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBandLevelLow() {
|
||||||
|
return mMinLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBandLevelHigh() {
|
||||||
|
return mMaxLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNumberOfBands() {
|
||||||
|
return (int) mEqualizer.getNumberOfBands();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCenterFreq(int band) {
|
||||||
|
return (int) mEqualizer.getCenterFreq((short) band);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBandLevel(int band) {
|
||||||
|
return (int) mEqualizer.getBandLevel((short) band);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBandLevel(int band, int level) {
|
||||||
|
mEqualizer.setBandLevel((short) band, (short) level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBassBoostEnabled() {
|
||||||
|
return mBassBoost.getEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBassBoostEnabled(boolean isEnabled) {
|
||||||
|
mBassBoost.setEnabled(isEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBassBoostStrength() {
|
||||||
|
return (int) mBassBoost.getRoundedStrength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBassBoostStrength(int strength) {
|
||||||
|
mBassBoost.setStrength((short) strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVirtualizerEnabled() {
|
||||||
|
return mVirtualizer.getEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVirtualizerEnabled(boolean isEnabled) {
|
||||||
|
mVirtualizer.setEnabled(isEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVirtualizerStrength() {
|
||||||
|
return mVirtualizer.getRoundedStrength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVirtualizerStrength(int strength) {
|
||||||
|
mVirtualizer.setStrength((short) strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
|
||||||
|
|
||||||
|
public class HorizontalAdapterHelper {
|
||||||
|
|
||||||
|
public static final int LAYOUT_RES = R.layout.item_image;
|
||||||
|
|
||||||
|
public static final int TYPE_FIRST = 1;
|
||||||
|
public static final int TYPE_MIDDLE = 2;
|
||||||
|
public static final int TYPE_LAST = 3;
|
||||||
|
|
||||||
|
public static void applyMarginToLayoutParams(Context context,
|
||||||
|
ViewGroup.MarginLayoutParams layoutParams, int viewType) {
|
||||||
|
int listMargin = context.getResources()
|
||||||
|
.getDimensionPixelSize(R.dimen.now_playing_top_margin);
|
||||||
|
if (viewType == TYPE_FIRST) {
|
||||||
|
layoutParams.leftMargin = listMargin;
|
||||||
|
} else if (viewType == TYPE_LAST) {
|
||||||
|
layoutParams.rightMargin = listMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getItemViewtype(int position, int itemCount) {
|
||||||
|
if (position == 0) {
|
||||||
|
return TYPE_FIRST;
|
||||||
|
} else if (position == itemCount - 1) {
|
||||||
|
return TYPE_LAST;
|
||||||
|
} else {
|
||||||
|
return TYPE_MIDDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
public interface M3UConstants {
|
||||||
|
String EXTENSION = "m3u";
|
||||||
|
String HEADER = "#EXTM3U";
|
||||||
|
String ENTRY = "#EXTINF:";
|
||||||
|
String DURATION_SEPARATOR = ",";
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader;
|
||||||
|
import code.name.monkey.retromusic.model.AbsCustomPlaylist;
|
||||||
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.ObservableEmitter;
|
||||||
|
|
||||||
|
public class M3UWriter implements M3UConstants {
|
||||||
|
public static final String TAG = M3UWriter.class.getSimpleName();
|
||||||
|
|
||||||
|
public static Observable<File> write(@NonNull Context context,
|
||||||
|
@NonNull File dir, @NonNull Playlist playlist) {
|
||||||
|
if (!dir.exists()) //noinspection ResultOfMethodCallIgnored
|
||||||
|
dir.mkdirs();
|
||||||
|
File file = new File(dir, playlist.name.concat("." + EXTENSION));
|
||||||
|
|
||||||
|
if (playlist instanceof AbsCustomPlaylist) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
((AbsCustomPlaylist) playlist).getSongs(context).subscribe(songs -> {
|
||||||
|
saveSongsToFile(file, e, songs);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
return Observable.create(e ->
|
||||||
|
PlaylistSongsLoader.getPlaylistSongList(context, playlist.id)
|
||||||
|
.subscribe(songs -> {
|
||||||
|
saveSongsToFile(file, e, songs);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void saveSongsToFile(File file, ObservableEmitter<File> e, ArrayList<Song> songs) throws IOException {
|
||||||
|
if (songs.size() > 0) {
|
||||||
|
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
|
||||||
|
bw.write(HEADER);
|
||||||
|
for (Song song : songs) {
|
||||||
|
bw.newLine();
|
||||||
|
bw.write(ENTRY + song.duration + DURATION_SEPARATOR + song.artistName + " - " + song.title);
|
||||||
|
bw.newLine();
|
||||||
|
bw.write(song.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bw.close();
|
||||||
|
}
|
||||||
|
e.onNext(file);
|
||||||
|
e.onComplete();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,480 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.loaders.SongLoader;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.service.MusicService;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
|
||||||
|
public class MusicPlayerRemote {
|
||||||
|
|
||||||
|
public static final String TAG = MusicPlayerRemote.class.getSimpleName();
|
||||||
|
private static final WeakHashMap<Context, ServiceBinder> mConnectionMap = new WeakHashMap<>();
|
||||||
|
@Nullable
|
||||||
|
public static MusicService musicService;
|
||||||
|
|
||||||
|
public static ServiceToken bindToService(@NonNull final Context context,
|
||||||
|
final ServiceConnection callback) {
|
||||||
|
Activity realActivity = ((Activity) context).getParent();
|
||||||
|
if (realActivity == null) {
|
||||||
|
realActivity = (Activity) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ContextWrapper contextWrapper = new ContextWrapper(realActivity);
|
||||||
|
contextWrapper.startService(new Intent(contextWrapper, MusicService.class));
|
||||||
|
|
||||||
|
final ServiceBinder binder = new ServiceBinder(callback);
|
||||||
|
|
||||||
|
if (contextWrapper.bindService(new Intent().setClass(contextWrapper, MusicService.class), binder, Context.BIND_AUTO_CREATE)) {
|
||||||
|
mConnectionMap.put(contextWrapper, binder);
|
||||||
|
return new ServiceToken(contextWrapper);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unbindFromService(@Nullable final ServiceToken token) {
|
||||||
|
if (token == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ContextWrapper mContextWrapper = token.mWrappedContext;
|
||||||
|
final ServiceBinder mBinder = mConnectionMap.remove(mContextWrapper);
|
||||||
|
if (mBinder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mContextWrapper.unbindService(mBinder);
|
||||||
|
if (mConnectionMap.isEmpty()) {
|
||||||
|
musicService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static String getFilePathFromUri(Context context, Uri uri) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
final String column = "_data";
|
||||||
|
final String[] projection = {
|
||||||
|
column
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = context.getContentResolver().query(uri, projection, null, null,
|
||||||
|
null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
final int column_index = cursor.getColumnIndexOrThrow(column);
|
||||||
|
return cursor.getString(column_index);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async
|
||||||
|
*/
|
||||||
|
public static void playSongAt(final int position) {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.playSongAt(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void pauseSong() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async
|
||||||
|
*/
|
||||||
|
public static void playNextSong() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.playNextSong(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async
|
||||||
|
*/
|
||||||
|
public static void playPreviousSong() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.playPreviousSong(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async
|
||||||
|
*/
|
||||||
|
public static void back() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.back(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPlaying() {
|
||||||
|
return musicService != null && musicService.isPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resumePlaying() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async
|
||||||
|
*/
|
||||||
|
public static void openQueue(final ArrayList<Song> queue, final int startPosition, final boolean startPlaying) {
|
||||||
|
if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) {
|
||||||
|
musicService.openQueue(queue, startPosition, startPlaying);
|
||||||
|
if (PreferenceUtil.getInstance(musicService).isShuffleModeOn())
|
||||||
|
setShuffleMode(MusicService.SHUFFLE_MODE_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async
|
||||||
|
*/
|
||||||
|
public static void openAndShuffleQueue(final ArrayList<Song> queue, boolean startPlaying) {
|
||||||
|
int startPosition = 0;
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
startPosition = new Random().nextInt(queue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) {
|
||||||
|
openQueue(queue, startPosition, startPlaying);
|
||||||
|
setShuffleMode(MusicService.SHUFFLE_MODE_SHUFFLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean tryToHandleOpenPlayingQueue(final ArrayList<Song> queue, final int startPosition, final boolean startPlaying) {
|
||||||
|
if (getPlayingQueue() == queue) {
|
||||||
|
if (startPlaying) {
|
||||||
|
playSongAt(startPosition);
|
||||||
|
} else {
|
||||||
|
setPosition(startPosition);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Song getCurrentSong() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getCurrentSong();
|
||||||
|
}
|
||||||
|
return Song.EMPTY_SONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getPosition() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getPosition();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async
|
||||||
|
*/
|
||||||
|
public static void setPosition(final int position) {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.setPosition(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<Song> getPlayingQueue() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getPlayingQueue();
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSongProgressMillis() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getSongProgressMillis();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSongDurationMillis() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getSongDurationMillis();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getQueueDurationMillis(int position) {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getQueueDurationMillis(position);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int seekTo(int millis) {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.seek(millis);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRepeatMode() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getRepeatMode();
|
||||||
|
}
|
||||||
|
return MusicService.REPEAT_MODE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getShuffleMode() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getShuffleMode();
|
||||||
|
}
|
||||||
|
return MusicService.SHUFFLE_MODE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean cycleRepeatMode() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.cycleRepeatMode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean toggleShuffleMode() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.toggleShuffle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setShuffleMode(final int shuffleMode) {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.setShuffleMode(shuffleMode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean playNext(Song song) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSong(getPosition() + 1, song);
|
||||||
|
} else {
|
||||||
|
ArrayList<Song> queue = new ArrayList<>();
|
||||||
|
queue.add(song);
|
||||||
|
openQueue(queue, 0, false);
|
||||||
|
}
|
||||||
|
Toast.makeText(musicService, musicService.getResources().getString(R.string.added_title_to_playing_queue), Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean playNext(@NonNull ArrayList<Song> songs) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSongs(getPosition() + 1, songs);
|
||||||
|
} else {
|
||||||
|
openQueue(songs, 0, false);
|
||||||
|
}
|
||||||
|
final String toast = songs.size() == 1 ? musicService.getResources().getString(R.string.added_title_to_playing_queue) : musicService.getResources().getString(R.string.added_x_titles_to_playing_queue, songs.size());
|
||||||
|
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean enqueue(Song song) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSong(song);
|
||||||
|
} else {
|
||||||
|
ArrayList<Song> queue = new ArrayList<>();
|
||||||
|
queue.add(song);
|
||||||
|
openQueue(queue, 0, false);
|
||||||
|
}
|
||||||
|
Toast.makeText(musicService, musicService.getResources().getString(R.string.added_title_to_playing_queue), Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean enqueue(@NonNull ArrayList<Song> songs) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSongs(songs);
|
||||||
|
} else {
|
||||||
|
openQueue(songs, 0, false);
|
||||||
|
}
|
||||||
|
final String toast = songs.size() == 1 ? musicService.getResources().getString(R.string.added_title_to_playing_queue) : musicService.getResources().getString(R.string.added_x_titles_to_playing_queue, songs.size());
|
||||||
|
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean removeFromQueue(@NonNull Song song) {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.removeSong(song);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean removeFromQueue(int position) {
|
||||||
|
if (musicService != null && position >= 0 && position < getPlayingQueue().size()) {
|
||||||
|
musicService.removeSong(position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean moveSong(int from, int to) {
|
||||||
|
if (musicService != null && from >= 0 && to >= 0 && from < getPlayingQueue().size() && to < getPlayingQueue().size()) {
|
||||||
|
musicService.moveSong(from, to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean clearQueue() {
|
||||||
|
if (musicService != null) {
|
||||||
|
musicService.clearQueue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getAudioSessionId() {
|
||||||
|
if (musicService != null) {
|
||||||
|
return musicService.getAudioSessionId();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playFromUri(Uri uri) {
|
||||||
|
if (musicService != null) {
|
||||||
|
ArrayList<Song> songs = null;
|
||||||
|
if (uri.getScheme() != null && uri.getAuthority() != null) {
|
||||||
|
if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
|
||||||
|
String songId = null;
|
||||||
|
if (uri.getAuthority().equals("com.android.providers.media.documents")) {
|
||||||
|
songId = getSongIdFromMediaProvider(uri);
|
||||||
|
} else if (uri.getAuthority().equals("media")) {
|
||||||
|
songId = uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
if (songId != null) {
|
||||||
|
/* songs = SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||||
|
musicService,
|
||||||
|
MediaStore.Audio.AudioColumns._ID + "=?",
|
||||||
|
new String[]{songId}
|
||||||
|
));*/
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||||
|
musicService,
|
||||||
|
MediaStore.Audio.AudioColumns._ID + "=?",
|
||||||
|
new String[]{songId}))
|
||||||
|
.subscribeOn(Schedulers.io()).blockingFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (songs == null) {
|
||||||
|
File songFile = null;
|
||||||
|
if (uri.getAuthority() != null && uri.getAuthority().equals("com.android.externalstorage.documents")) {
|
||||||
|
songFile = new File(Environment.getExternalStorageDirectory(), uri.getPath().split(":", 2)[1]);
|
||||||
|
}
|
||||||
|
if (songFile == null) {
|
||||||
|
String path = getFilePathFromUri(musicService, uri);
|
||||||
|
if (path != null)
|
||||||
|
songFile = new File(path);
|
||||||
|
}
|
||||||
|
if (songFile == null && uri.getPath() != null) {
|
||||||
|
songFile = new File(uri.getPath());
|
||||||
|
}
|
||||||
|
if (songFile != null) {
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||||
|
musicService,
|
||||||
|
MediaStore.Audio.AudioColumns.DATA + "=?",
|
||||||
|
new String[]{songFile.getAbsolutePath()}
|
||||||
|
)).blockingFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (songs != null && !songs.isEmpty()) {
|
||||||
|
openQueue(songs, 0, true);
|
||||||
|
} else {
|
||||||
|
//TODO the file is not listed in the media store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||||
|
private static String getSongIdFromMediaProvider(Uri uri) {
|
||||||
|
return DocumentsContract.getDocumentId(uri).split(":")[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isServiceConnected() {
|
||||||
|
return musicService != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final class ServiceBinder implements ServiceConnection {
|
||||||
|
private final ServiceConnection mCallback;
|
||||||
|
|
||||||
|
ServiceBinder(final ServiceConnection callback) {
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(final ComponentName className, final IBinder service) {
|
||||||
|
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
|
||||||
|
musicService = binder.getService();
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onServiceConnected(className, service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(final ComponentName className) {
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onServiceDisconnected(className);
|
||||||
|
}
|
||||||
|
musicService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ServiceToken {
|
||||||
|
ContextWrapper mWrappedContext;
|
||||||
|
|
||||||
|
ServiceToken(final ContextWrapper context) {
|
||||||
|
mWrappedContext = context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
|
||||||
|
public class MusicProgressViewUpdateHelper extends Handler {
|
||||||
|
private static final int CMD_REFRESH_PROGRESS_VIEWS = 1;
|
||||||
|
|
||||||
|
private static final int MIN_INTERVAL = 20;
|
||||||
|
private static final int UPDATE_INTERVAL_PLAYING = 1000;
|
||||||
|
private static final int UPDATE_INTERVAL_PAUSED = 500;
|
||||||
|
|
||||||
|
private Callback callback;
|
||||||
|
private int intervalPlaying;
|
||||||
|
private int intervalPaused;
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
queueNextRefresh(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
removeMessages(CMD_REFRESH_PROGRESS_VIEWS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MusicProgressViewUpdateHelper(Callback callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.intervalPlaying = UPDATE_INTERVAL_PLAYING;
|
||||||
|
this.intervalPaused = UPDATE_INTERVAL_PAUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MusicProgressViewUpdateHelper(Callback callback, int intervalPlaying, int intervalPaused) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.intervalPlaying = intervalPlaying;
|
||||||
|
this.intervalPaused = intervalPaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(@NonNull Message msg) {
|
||||||
|
super.handleMessage(msg);
|
||||||
|
if (msg.what == CMD_REFRESH_PROGRESS_VIEWS) {
|
||||||
|
queueNextRefresh(refreshProgressViews());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int refreshProgressViews() {
|
||||||
|
final int progressMillis = MusicPlayerRemote.getSongProgressMillis();
|
||||||
|
final int totalMillis = MusicPlayerRemote.getSongDurationMillis();
|
||||||
|
|
||||||
|
callback.onUpdateProgressViews(progressMillis, totalMillis);
|
||||||
|
|
||||||
|
if (!MusicPlayerRemote.isPlaying()) {
|
||||||
|
return intervalPaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int remainingMillis = intervalPlaying - progressMillis % intervalPlaying;
|
||||||
|
|
||||||
|
return Math.max(MIN_INTERVAL, remainingMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueNextRefresh(final long delay) {
|
||||||
|
final Message message = obtainMessage(CMD_REFRESH_PROGRESS_VIEWS);
|
||||||
|
removeMessages(CMD_REFRESH_PROGRESS_VIEWS);
|
||||||
|
sendMessageDelayed(message, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
void onUpdateProgressViews(int progress, int total);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
|
||||||
|
public class PlayPauseButtonOnClickHandler implements View.OnClickListener {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (MusicPlayerRemote.isPlaying()) {
|
||||||
|
MusicPlayerRemote.pauseSong();
|
||||||
|
} else {
|
||||||
|
MusicPlayerRemote.resumePlaying();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.app.SearchManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.loaders.SongLoader;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
|
||||||
|
|
||||||
|
public class SearchQueryHelper {
|
||||||
|
private static final String TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?";
|
||||||
|
private static final String ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?";
|
||||||
|
private static final String ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?";
|
||||||
|
private static final String AND = " AND ";
|
||||||
|
private static ArrayList<Song> songs = new ArrayList<>();
|
||||||
|
|
||||||
|
public static ArrayList<Song> getSongs() {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setSongs(ArrayList<Song> songs) {
|
||||||
|
SearchQueryHelper.songs = songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ArrayList<Song> getSongs(@NonNull final Context context, @NonNull final Bundle extras) {
|
||||||
|
final String query = extras.getString(SearchManager.QUERY, null);
|
||||||
|
final String artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null);
|
||||||
|
final String albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null);
|
||||||
|
final String titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null);
|
||||||
|
|
||||||
|
ArrayList<Song> songs = new ArrayList<>();
|
||||||
|
if (artistName != null && albumName != null && titleName != null) {
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase(), albumName.toLowerCase(), titleName.toLowerCase()})).blockingFirst();
|
||||||
|
}
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
if (artistName != null && titleName != null) {
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase(), titleName.toLowerCase()})).blockingFirst();
|
||||||
|
}
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
if (albumName != null && titleName != null) {
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{albumName.toLowerCase(), titleName.toLowerCase()})).blockingFirst();
|
||||||
|
}
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
if (artistName != null) {
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, new String[]{artistName.toLowerCase()})).blockingFirst();
|
||||||
|
}
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
if (albumName != null) {
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, new String[]{albumName.toLowerCase()})).blockingFirst();
|
||||||
|
}
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
if (titleName != null) {
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, new String[]{titleName.toLowerCase()})).blockingFirst();
|
||||||
|
}
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, new String[]{query.toLowerCase()})).blockingFirst();
|
||||||
|
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, new String[]{query.toLowerCase()})).blockingFirst();
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, new String[]{query.toLowerCase()})).blockingFirst();
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
return new ArrayList<Song>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class ShuffleHelper {
|
||||||
|
|
||||||
|
public static void makeShuffleList(@NonNull List<Song> listToShuffle, final int current) {
|
||||||
|
if (listToShuffle.isEmpty()) return;
|
||||||
|
if (current >= 0) {
|
||||||
|
Song song = listToShuffle.remove(current);
|
||||||
|
Collections.shuffle(listToShuffle);
|
||||||
|
listToShuffle.add(0, song);
|
||||||
|
} else {
|
||||||
|
Collections.shuffle(listToShuffle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Andrew Neal 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 code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds all of the sort orders for each list type.
|
||||||
|
*
|
||||||
|
* @author Andrew Neal (andrewdneal@gmail.com)
|
||||||
|
*/
|
||||||
|
public final class SortOrder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is never instantiated
|
||||||
|
*/
|
||||||
|
public SortOrder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Artist sort order entries.
|
||||||
|
*/
|
||||||
|
public interface ArtistSortOrder {
|
||||||
|
|
||||||
|
/* Artist sort order A-Z */
|
||||||
|
String ARTIST_A_Z = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Artist sort order Z-A */
|
||||||
|
String ARTIST_Z_A = ARTIST_A_Z + " DESC";
|
||||||
|
|
||||||
|
/* Artist sort order number of songs */
|
||||||
|
String ARTIST_NUMBER_OF_SONGS = MediaStore.Audio.Artists.NUMBER_OF_TRACKS
|
||||||
|
+ " DESC";
|
||||||
|
|
||||||
|
/* Artist sort order number of albums */
|
||||||
|
String ARTIST_NUMBER_OF_ALBUMS = MediaStore.Audio.Artists.NUMBER_OF_ALBUMS
|
||||||
|
+ " DESC";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Album sort order entries.
|
||||||
|
*/
|
||||||
|
public interface AlbumSortOrder {
|
||||||
|
|
||||||
|
/* Album sort order A-Z */
|
||||||
|
String ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Album sort order Z-A */
|
||||||
|
String ALBUM_Z_A = ALBUM_A_Z + " DESC";
|
||||||
|
|
||||||
|
/* Album sort order songs */
|
||||||
|
String ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.Albums.NUMBER_OF_SONGS
|
||||||
|
+ " DESC";
|
||||||
|
|
||||||
|
/* Album sort order artist */
|
||||||
|
String ALBUM_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER
|
||||||
|
+ ", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Album sort order year */
|
||||||
|
String ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Song sort order entries.
|
||||||
|
*/
|
||||||
|
public interface SongSortOrder {
|
||||||
|
|
||||||
|
/* Song sort order A-Z */
|
||||||
|
String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Song sort order Z-A */
|
||||||
|
String SONG_Z_A = SONG_A_Z + " DESC";
|
||||||
|
|
||||||
|
/* Song sort order artist */
|
||||||
|
String SONG_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Song sort order album */
|
||||||
|
String SONG_ALBUM = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Song sort order year */
|
||||||
|
String SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC";
|
||||||
|
|
||||||
|
/* Song sort order duration */
|
||||||
|
String SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC";
|
||||||
|
|
||||||
|
/* Song sort order date */
|
||||||
|
String SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Album song sort order entries.
|
||||||
|
*/
|
||||||
|
public interface AlbumSongSortOrder {
|
||||||
|
|
||||||
|
/* Album song sort order A-Z */
|
||||||
|
String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Album song sort order Z-A */
|
||||||
|
String SONG_Z_A = SONG_A_Z + " DESC";
|
||||||
|
|
||||||
|
/* Album song sort order track list */
|
||||||
|
String SONG_TRACK_LIST = MediaStore.Audio.Media.TRACK + ", "
|
||||||
|
+ MediaStore.Audio.Media.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Album song sort order duration */
|
||||||
|
String SONG_DURATION = SongSortOrder.SONG_DURATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Artist song sort order entries.
|
||||||
|
*/
|
||||||
|
public interface ArtistSongSortOrder {
|
||||||
|
|
||||||
|
/* Artist song sort order A-Z */
|
||||||
|
String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Artist song sort order Z-A */
|
||||||
|
String SONG_Z_A = SONG_A_Z + " DESC";
|
||||||
|
|
||||||
|
/* Artist song sort order album */
|
||||||
|
String SONG_ALBUM = MediaStore.Audio.Media.ALBUM;
|
||||||
|
|
||||||
|
/* Artist song sort order year */
|
||||||
|
String SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC";
|
||||||
|
|
||||||
|
/* Artist song sort order duration */
|
||||||
|
String SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC";
|
||||||
|
|
||||||
|
/* Artist song sort order date */
|
||||||
|
String SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Artist album sort order entries.
|
||||||
|
*/
|
||||||
|
public interface ArtistAlbumSortOrder {
|
||||||
|
|
||||||
|
/* Artist album sort order A-Z */
|
||||||
|
String ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Artist album sort order Z-A */
|
||||||
|
String ALBUM_Z_A = ALBUM_A_Z + " DESC";
|
||||||
|
|
||||||
|
/* Artist album sort order year */
|
||||||
|
String ALBUM_YEAR = MediaStore.Audio.Media.YEAR
|
||||||
|
+ " DESC";
|
||||||
|
|
||||||
|
/* Artist album sort order year */
|
||||||
|
String ALBUM_YEAR_ASC = MediaStore.Audio.Media.YEAR
|
||||||
|
+ " ASC";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Genre sort order entries.
|
||||||
|
*/
|
||||||
|
public interface GenreSortOrder {
|
||||||
|
|
||||||
|
/* Genre sort order A-Z */
|
||||||
|
String GENRE_A_Z = MediaStore.Audio.Genres.DEFAULT_SORT_ORDER;
|
||||||
|
|
||||||
|
/* Genre sort order Z-A */
|
||||||
|
String ALBUM_Z_A = GENRE_A_Z + " DESC";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,333 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blur using Java code.
|
||||||
|
* <p/>
|
||||||
|
* This is a compromise between Gaussian Blur and Box blur
|
||||||
|
* It creates much better looking blurs than Box Blur, but is
|
||||||
|
* 7x faster than my Gaussian Blur implementation.
|
||||||
|
* <p/>
|
||||||
|
* I called it Stack Blur because this describes best how this
|
||||||
|
* filter works internally: it creates a kind of moving stack
|
||||||
|
* of colors whilst scanning through the image. Thereby it
|
||||||
|
* just has to add one new block of color to the right side
|
||||||
|
* of the stack and remove the leftmost color. The remaining
|
||||||
|
* colors on the topmost layer of the stack are either added on
|
||||||
|
* or reduced by one, depending on if they are on the right or
|
||||||
|
* on the left side of the stack.
|
||||||
|
*
|
||||||
|
* @author Enrique López Mañas <eenriquelopez@gmail.com>
|
||||||
|
* http://www.neo-tech.es
|
||||||
|
* <p/>
|
||||||
|
* Author of the original algorithm: Mario Klingemann <mario.quasimondo.com>
|
||||||
|
* <p/>
|
||||||
|
* Based heavily on http://vitiy.info/Code/stackblur.cpp
|
||||||
|
* See http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/
|
||||||
|
* @copyright: Enrique López Mañas
|
||||||
|
* @license: Apache License 2.0
|
||||||
|
*/
|
||||||
|
public class StackBlur {
|
||||||
|
|
||||||
|
static final int EXECUTOR_THREADS = Runtime.getRuntime().availableProcessors();
|
||||||
|
static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(EXECUTOR_THREADS);
|
||||||
|
|
||||||
|
private static final short[] stackblur_mul = {
|
||||||
|
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
|
||||||
|
454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
|
||||||
|
482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
|
||||||
|
437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
|
||||||
|
497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
|
||||||
|
320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
|
||||||
|
446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
|
||||||
|
329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
|
||||||
|
505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
|
||||||
|
399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
|
||||||
|
324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
|
||||||
|
268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
|
||||||
|
451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
|
||||||
|
385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
|
||||||
|
332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
|
||||||
|
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final byte[] stackblur_shr = {
|
||||||
|
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
|
||||||
|
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
|
||||||
|
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
|
||||||
|
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
|
||||||
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||||
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
|
||||||
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||||
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
|
||||||
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Bitmap blur(Bitmap original, float radius) {
|
||||||
|
int w = original.getWidth();
|
||||||
|
int h = original.getHeight();
|
||||||
|
int[] currentPixels = new int[w * h];
|
||||||
|
original.getPixels(currentPixels, 0, w, 0, 0, w, h);
|
||||||
|
int cores = EXECUTOR_THREADS;
|
||||||
|
|
||||||
|
ArrayList<BlurTask> horizontal = new ArrayList<BlurTask>(cores);
|
||||||
|
ArrayList<BlurTask> vertical = new ArrayList<BlurTask>(cores);
|
||||||
|
for (int i = 0; i < cores; i++) {
|
||||||
|
horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1));
|
||||||
|
vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
EXECUTOR.invokeAll(horizontal);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
EXECUTOR.invokeAll(vertical);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Bitmap.createBitmap(currentPixels, w, h, Bitmap.Config.ARGB_8888);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void blurIteration(int[] src, int w, int h, int radius, int cores, int core, int step) {
|
||||||
|
int x, y, xp, yp, i;
|
||||||
|
int sp;
|
||||||
|
int stack_start;
|
||||||
|
int stack_i;
|
||||||
|
|
||||||
|
int src_i;
|
||||||
|
int dst_i;
|
||||||
|
|
||||||
|
long sum_r, sum_g, sum_b,
|
||||||
|
sum_in_r, sum_in_g, sum_in_b,
|
||||||
|
sum_out_r, sum_out_g, sum_out_b;
|
||||||
|
|
||||||
|
int wm = w - 1;
|
||||||
|
int hm = h - 1;
|
||||||
|
int div = (radius * 2) + 1;
|
||||||
|
int mul_sum = stackblur_mul[radius];
|
||||||
|
byte shr_sum = stackblur_shr[radius];
|
||||||
|
int[] stack = new int[div];
|
||||||
|
|
||||||
|
if (step == 1) {
|
||||||
|
int minY = core * h / cores;
|
||||||
|
int maxY = (core + 1) * h / cores;
|
||||||
|
|
||||||
|
for (y = minY; y < maxY; y++) {
|
||||||
|
sum_r = sum_g = sum_b =
|
||||||
|
sum_in_r = sum_in_g = sum_in_b =
|
||||||
|
sum_out_r = sum_out_g = sum_out_b = 0;
|
||||||
|
|
||||||
|
src_i = w * y; // start of line (0,y)
|
||||||
|
|
||||||
|
for (i = 0; i <= radius; i++) {
|
||||||
|
stack_i = i;
|
||||||
|
stack[stack_i] = src[src_i];
|
||||||
|
sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1);
|
||||||
|
sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1);
|
||||||
|
sum_b += (src[src_i] & 0xff) * (i + 1);
|
||||||
|
sum_out_r += ((src[src_i] >>> 16) & 0xff);
|
||||||
|
sum_out_g += ((src[src_i] >>> 8) & 0xff);
|
||||||
|
sum_out_b += (src[src_i] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; i++) {
|
||||||
|
if (i <= wm) src_i += 1;
|
||||||
|
stack_i = i + radius;
|
||||||
|
stack[stack_i] = src[src_i];
|
||||||
|
sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i);
|
||||||
|
sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i);
|
||||||
|
sum_b += (src[src_i] & 0xff) * (radius + 1 - i);
|
||||||
|
sum_in_r += ((src[src_i] >>> 16) & 0xff);
|
||||||
|
sum_in_g += ((src[src_i] >>> 8) & 0xff);
|
||||||
|
sum_in_b += (src[src_i] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sp = radius;
|
||||||
|
xp = radius;
|
||||||
|
if (xp > wm) xp = wm;
|
||||||
|
src_i = xp + y * w; // img.pix_ptr(xp, y);
|
||||||
|
dst_i = y * w; // img.pix_ptr(0, y);
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
src[dst_i] = (int)
|
||||||
|
((src[dst_i] & 0xFFFFFFFF) |
|
||||||
|
((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) |
|
||||||
|
((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) |
|
||||||
|
((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
|
||||||
|
dst_i += 1;
|
||||||
|
|
||||||
|
sum_r -= sum_out_r;
|
||||||
|
sum_g -= sum_out_g;
|
||||||
|
sum_b -= sum_out_b;
|
||||||
|
|
||||||
|
stack_start = sp + div - radius;
|
||||||
|
if (stack_start >= div) stack_start -= div;
|
||||||
|
stack_i = stack_start;
|
||||||
|
|
||||||
|
sum_out_r -= ((stack[stack_i] >>> 16) & 0xff);
|
||||||
|
sum_out_g -= ((stack[stack_i] >>> 8) & 0xff);
|
||||||
|
sum_out_b -= (stack[stack_i] & 0xff);
|
||||||
|
|
||||||
|
if (xp < wm) {
|
||||||
|
src_i += 1;
|
||||||
|
++xp;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[stack_i] = src[src_i];
|
||||||
|
|
||||||
|
sum_in_r += ((src[src_i] >>> 16) & 0xff);
|
||||||
|
sum_in_g += ((src[src_i] >>> 8) & 0xff);
|
||||||
|
sum_in_b += (src[src_i] & 0xff);
|
||||||
|
sum_r += sum_in_r;
|
||||||
|
sum_g += sum_in_g;
|
||||||
|
sum_b += sum_in_b;
|
||||||
|
|
||||||
|
++sp;
|
||||||
|
if (sp >= div) sp = 0;
|
||||||
|
stack_i = sp;
|
||||||
|
|
||||||
|
sum_out_r += ((stack[stack_i] >>> 16) & 0xff);
|
||||||
|
sum_out_g += ((stack[stack_i] >>> 8) & 0xff);
|
||||||
|
sum_out_b += (stack[stack_i] & 0xff);
|
||||||
|
sum_in_r -= ((stack[stack_i] >>> 16) & 0xff);
|
||||||
|
sum_in_g -= ((stack[stack_i] >>> 8) & 0xff);
|
||||||
|
sum_in_b -= (stack[stack_i] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 2
|
||||||
|
else if (step == 2) {
|
||||||
|
int minX = core * w / cores;
|
||||||
|
int maxX = (core + 1) * w / cores;
|
||||||
|
|
||||||
|
for (x = minX; x < maxX; x++) {
|
||||||
|
sum_r = sum_g = sum_b =
|
||||||
|
sum_in_r = sum_in_g = sum_in_b =
|
||||||
|
sum_out_r = sum_out_g = sum_out_b = 0;
|
||||||
|
|
||||||
|
src_i = x; // x,0
|
||||||
|
for (i = 0; i <= radius; i++) {
|
||||||
|
stack_i = i;
|
||||||
|
stack[stack_i] = src[src_i];
|
||||||
|
sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1);
|
||||||
|
sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1);
|
||||||
|
sum_b += (src[src_i] & 0xff) * (i + 1);
|
||||||
|
sum_out_r += ((src[src_i] >>> 16) & 0xff);
|
||||||
|
sum_out_g += ((src[src_i] >>> 8) & 0xff);
|
||||||
|
sum_out_b += (src[src_i] & 0xff);
|
||||||
|
}
|
||||||
|
for (i = 1; i <= radius; i++) {
|
||||||
|
if (i <= hm) src_i += w; // +stride
|
||||||
|
|
||||||
|
stack_i = i + radius;
|
||||||
|
stack[stack_i] = src[src_i];
|
||||||
|
sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i);
|
||||||
|
sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i);
|
||||||
|
sum_b += (src[src_i] & 0xff) * (radius + 1 - i);
|
||||||
|
sum_in_r += ((src[src_i] >>> 16) & 0xff);
|
||||||
|
sum_in_g += ((src[src_i] >>> 8) & 0xff);
|
||||||
|
sum_in_b += (src[src_i] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = radius;
|
||||||
|
yp = radius;
|
||||||
|
if (yp > hm) yp = hm;
|
||||||
|
src_i = x + yp * w; // img.pix_ptr(x, yp);
|
||||||
|
dst_i = x; // img.pix_ptr(x, 0);
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
src[dst_i] = (int)
|
||||||
|
((src[dst_i] & 0xFFFFFFFF) |
|
||||||
|
((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) |
|
||||||
|
((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) |
|
||||||
|
((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
|
||||||
|
dst_i += w;
|
||||||
|
|
||||||
|
sum_r -= sum_out_r;
|
||||||
|
sum_g -= sum_out_g;
|
||||||
|
sum_b -= sum_out_b;
|
||||||
|
|
||||||
|
stack_start = sp + div - radius;
|
||||||
|
if (stack_start >= div) stack_start -= div;
|
||||||
|
stack_i = stack_start;
|
||||||
|
|
||||||
|
sum_out_r -= ((stack[stack_i] >>> 16) & 0xff);
|
||||||
|
sum_out_g -= ((stack[stack_i] >>> 8) & 0xff);
|
||||||
|
sum_out_b -= (stack[stack_i] & 0xff);
|
||||||
|
|
||||||
|
if (yp < hm) {
|
||||||
|
src_i += w; // stride
|
||||||
|
++yp;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[stack_i] = src[src_i];
|
||||||
|
|
||||||
|
sum_in_r += ((src[src_i] >>> 16) & 0xff);
|
||||||
|
sum_in_g += ((src[src_i] >>> 8) & 0xff);
|
||||||
|
sum_in_b += (src[src_i] & 0xff);
|
||||||
|
sum_r += sum_in_r;
|
||||||
|
sum_g += sum_in_g;
|
||||||
|
sum_b += sum_in_b;
|
||||||
|
|
||||||
|
++sp;
|
||||||
|
if (sp >= div) sp = 0;
|
||||||
|
stack_i = sp;
|
||||||
|
|
||||||
|
sum_out_r += ((stack[stack_i] >>> 16) & 0xff);
|
||||||
|
sum_out_g += ((stack[stack_i] >>> 8) & 0xff);
|
||||||
|
sum_out_b += (stack[stack_i] & 0xff);
|
||||||
|
sum_in_r -= ((stack[stack_i] >>> 16) & 0xff);
|
||||||
|
sum_in_g -= ((stack[stack_i] >>> 8) & 0xff);
|
||||||
|
sum_in_b -= (stack[stack_i] & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BlurTask implements Callable<Void> {
|
||||||
|
private final int[] _src;
|
||||||
|
private final int _w;
|
||||||
|
private final int _h;
|
||||||
|
private final int _radius;
|
||||||
|
private final int _totalCores;
|
||||||
|
private final int _coreIndex;
|
||||||
|
private final int _round;
|
||||||
|
|
||||||
|
public BlurTask(int[] src, int w, int h, int radius, int totalCores, int coreIndex, int round) {
|
||||||
|
_src = src;
|
||||||
|
_w = w;
|
||||||
|
_h = h;
|
||||||
|
_radius = radius;
|
||||||
|
_totalCores = totalCores;
|
||||||
|
_coreIndex = coreIndex;
|
||||||
|
_round = round;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void call() throws Exception {
|
||||||
|
blurIteration(_src, _w, _h, _radius, _totalCores, _coreIndex, _round);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package code.name.monkey.retromusic.helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple thread safe stop watch.
|
||||||
|
*
|
||||||
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
|
*/
|
||||||
|
public class StopWatch {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time the stop watch was last started.
|
||||||
|
*/
|
||||||
|
private long startTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time elapsed before the current {@link #startTime}.
|
||||||
|
*/
|
||||||
|
private long previousElapsedTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the stop watch is currently running or not.
|
||||||
|
*/
|
||||||
|
private boolean isRunning;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts or continues the stop watch.
|
||||||
|
*
|
||||||
|
* @see #pause()
|
||||||
|
* @see #reset()
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
synchronized (this) {
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses the stop watch. It can be continued later from {@link #start()}.
|
||||||
|
*
|
||||||
|
* @see #start()
|
||||||
|
* @see #reset()
|
||||||
|
*/
|
||||||
|
public void pause() {
|
||||||
|
synchronized (this) {
|
||||||
|
previousElapsedTime += System.currentTimeMillis() - startTime;
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops and resets the stop watch to zero milliseconds.
|
||||||
|
*
|
||||||
|
* @see #start()
|
||||||
|
* @see #pause()
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
synchronized (this) {
|
||||||
|
startTime = 0;
|
||||||
|
previousElapsedTime = 0;
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the total elapsed time in milliseconds
|
||||||
|
*/
|
||||||
|
public final long getElapsedTime() {
|
||||||
|
synchronized (this) {
|
||||||
|
long currentElapsedTime = 0;
|
||||||
|
if (isRunning) {
|
||||||
|
currentElapsedTime = System.currentTimeMillis() - startTime;
|
||||||
|
}
|
||||||
|
return previousElapsedTime + currentElapsedTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%d millis", getElapsedTime());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package code.name.monkey.retromusic.helper.menu;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.loaders.GenreLoader;
|
||||||
|
import code.name.monkey.retromusic.model.Genre;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog;
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Hemanth S (h4h13).
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class GenreMenuHelper {
|
||||||
|
public static boolean handleMenuClick(@NonNull AppCompatActivity activity,
|
||||||
|
@NonNull Genre genre,
|
||||||
|
@NonNull MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_play:
|
||||||
|
MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true);
|
||||||
|
return true;
|
||||||
|
case R.id.action_play_next:
|
||||||
|
MusicPlayerRemote.playNext(getGenreSongs(activity, genre));
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_playlist:
|
||||||
|
AddToPlaylistDialog.create(getGenreSongs(activity, genre))
|
||||||
|
.show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_current_playing:
|
||||||
|
MusicPlayerRemote.enqueue(getGenreSongs(activity, genre));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static ArrayList<Song> getGenreSongs(@NonNull Activity activity,
|
||||||
|
@NonNull Genre genre) {
|
||||||
|
ArrayList<Song> songs;
|
||||||
|
songs = GenreLoader.getSongs(activity, genre.id).blockingFirst();
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package code.name.monkey.retromusic.helper.menu;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.RetroApplication;
|
||||||
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog;
|
||||||
|
import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog;
|
||||||
|
import code.name.monkey.retromusic.dialogs.RenamePlaylistDialog;
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
|
||||||
|
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader;
|
||||||
|
import code.name.monkey.retromusic.misc.WeakContextAsyncTask;
|
||||||
|
import code.name.monkey.retromusic.model.AbsCustomPlaylist;
|
||||||
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.PlaylistsUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class PlaylistMenuHelper {
|
||||||
|
|
||||||
|
public static boolean handleMenuClick(@NonNull AppCompatActivity activity,
|
||||||
|
@NonNull final Playlist playlist, @NonNull MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_play:
|
||||||
|
MusicPlayerRemote.openQueue(getPlaylistSongs(activity, playlist), 9, true);
|
||||||
|
return true;
|
||||||
|
case R.id.action_play_next:
|
||||||
|
MusicPlayerRemote.playNext(getPlaylistSongs(activity, playlist));
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_playlist:
|
||||||
|
AddToPlaylistDialog.create(getPlaylistSongs(activity, playlist))
|
||||||
|
.show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_current_playing:
|
||||||
|
MusicPlayerRemote.enqueue(getPlaylistSongs(activity, playlist));
|
||||||
|
return true;
|
||||||
|
case R.id.action_rename_playlist:
|
||||||
|
RenamePlaylistDialog.create(playlist.id)
|
||||||
|
.show(activity.getSupportFragmentManager(), "RENAME_PLAYLIST");
|
||||||
|
return true;
|
||||||
|
case R.id.action_delete_playlist:
|
||||||
|
DeletePlaylistDialog.create(playlist)
|
||||||
|
.show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST");
|
||||||
|
return true;
|
||||||
|
case R.id.action_save_playlist:
|
||||||
|
new SavePlaylistAsyncTask(activity).execute(playlist);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static ArrayList<Song> getPlaylistSongs(@NonNull Activity activity,
|
||||||
|
@NonNull Playlist playlist) {
|
||||||
|
ArrayList<Song> songs;
|
||||||
|
if (playlist instanceof AbsCustomPlaylist) {
|
||||||
|
songs = ((AbsCustomPlaylist) playlist).getSongs(activity).blockingFirst();
|
||||||
|
} else {
|
||||||
|
songs = PlaylistSongsLoader.getPlaylistSongList(activity, playlist).blockingFirst();
|
||||||
|
}
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SavePlaylistAsyncTask extends WeakContextAsyncTask<Playlist, String, String> {
|
||||||
|
SavePlaylistAsyncTask(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(Playlist... params) {
|
||||||
|
return String.format(RetroApplication.getInstance().getApplicationContext().getString(R.string
|
||||||
|
.saved_playlist_to), PlaylistsUtil.savePlaylist(RetroApplication.getInstance().getApplicationContext(), params[0]).blockingFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String string) {
|
||||||
|
super.onPostExecute(string);
|
||||||
|
Context context = getContext();
|
||||||
|
if (context != null) {
|
||||||
|
Toast.makeText(context, string, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package code.name.monkey.retromusic.helper.menu;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog;
|
||||||
|
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog;
|
||||||
|
import code.name.monkey.retromusic.dialogs.SongDetailDialog;
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
|
||||||
|
import code.name.monkey.retromusic.interfaces.PaletteColorHolder;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.ui.activities.tageditor.AbsTagEditorActivity;
|
||||||
|
import code.name.monkey.retromusic.ui.activities.tageditor.SongTagEditorActivity;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.util.NavigationUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class SongMenuHelper {
|
||||||
|
public static final int MENU_RES = R.menu.menu_item_song;
|
||||||
|
|
||||||
|
public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull Song song, int menuItemId) {
|
||||||
|
switch (menuItemId) {
|
||||||
|
case R.id.action_set_as_ringtone:
|
||||||
|
MusicUtil.setRingtone(activity, song.id);
|
||||||
|
return true;
|
||||||
|
case R.id.action_share:
|
||||||
|
activity.startActivity(Intent.createChooser(MusicUtil.createShareSongFileIntent(song, activity), null));
|
||||||
|
return true;
|
||||||
|
case R.id.action_delete_from_device:
|
||||||
|
DeleteSongsDialog.create(song).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_playlist:
|
||||||
|
AddToPlaylistDialog.create(song).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
||||||
|
return true;
|
||||||
|
case R.id.action_play_next:
|
||||||
|
MusicPlayerRemote.playNext(song);
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_current_playing:
|
||||||
|
MusicPlayerRemote.enqueue(song);
|
||||||
|
return true;
|
||||||
|
case R.id.action_tag_editor:
|
||||||
|
Intent tagEditorIntent = new Intent(activity, SongTagEditorActivity.class);
|
||||||
|
tagEditorIntent.putExtra(AbsTagEditorActivity.EXTRA_ID, song.id);
|
||||||
|
if (activity instanceof PaletteColorHolder)
|
||||||
|
tagEditorIntent.putExtra(AbsTagEditorActivity.EXTRA_PALETTE, ((PaletteColorHolder) activity).getPaletteColor());
|
||||||
|
activity.startActivity(tagEditorIntent);
|
||||||
|
return true;
|
||||||
|
case R.id.action_details:
|
||||||
|
SongDetailDialog.create(song).show(activity.getSupportFragmentManager(), "SONG_DETAILS");
|
||||||
|
return true;
|
||||||
|
case R.id.action_go_to_album:
|
||||||
|
NavigationUtil.goToAlbum(activity, song.albumId);
|
||||||
|
return true;
|
||||||
|
case R.id.action_go_to_artist:
|
||||||
|
NavigationUtil.goToArtist(activity, song.artistId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class OnClickSongMenu implements View.OnClickListener, PopupMenu.OnMenuItemClickListener {
|
||||||
|
private AppCompatActivity activity;
|
||||||
|
|
||||||
|
protected OnClickSongMenu(@NonNull AppCompatActivity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMenuRes() {
|
||||||
|
return MENU_RES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
PopupMenu popupMenu = new PopupMenu(activity, v);
|
||||||
|
popupMenu.inflate(getMenuRes());
|
||||||
|
popupMenu.setOnMenuItemClickListener(this);
|
||||||
|
popupMenu.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
return handleMenuClick(activity, getSong(), item.getItemId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Song getSong();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package code.name.monkey.retromusic.helper.menu;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog;
|
||||||
|
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog;
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class SongsMenuHelper {
|
||||||
|
public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull ArrayList<Song> songs, int menuItemId) {
|
||||||
|
switch (menuItemId) {
|
||||||
|
case R.id.action_play_next:
|
||||||
|
MusicPlayerRemote.playNext(songs);
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_current_playing:
|
||||||
|
MusicPlayerRemote.enqueue(songs);
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_playlist:
|
||||||
|
AddToPlaylistDialog.create(songs).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
||||||
|
return true;
|
||||||
|
case R.id.action_delete_from_device:
|
||||||
|
DeleteSongsDialog.create(songs).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package code.name.monkey.retromusic.interfaces;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.afollestad.materialcab.MaterialCab;
|
||||||
|
|
||||||
|
|
||||||
|
public interface CabHolder {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
MaterialCab openCab(final int menuRes, final MaterialCab.Callback callback);
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package code.name.monkey.retromusic.interfaces;
|
||||||
|
|
||||||
|
import android.media.audiofx.BassBoost;
|
||||||
|
import android.media.audiofx.Equalizer;
|
||||||
|
import android.media.audiofx.Virtualizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Hemanth S (h4h13).
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface EqualizerInterface {
|
||||||
|
int getBandLevelLow();
|
||||||
|
|
||||||
|
int getBandLevelHigh();
|
||||||
|
|
||||||
|
int getNumberOfBands();
|
||||||
|
|
||||||
|
int getCenterFreq(int band);
|
||||||
|
|
||||||
|
int getBandLevel(int band);
|
||||||
|
|
||||||
|
void setBandLevel(int band, int level);
|
||||||
|
|
||||||
|
boolean isBassBoostEnabled();
|
||||||
|
|
||||||
|
void setBassBoostEnabled(boolean isEnabled);
|
||||||
|
|
||||||
|
int getBassBoostStrength();
|
||||||
|
|
||||||
|
void setBassBoostStrength(int strength);
|
||||||
|
|
||||||
|
boolean isVirtualizerEnabled();
|
||||||
|
|
||||||
|
void setVirtualizerEnabled(boolean isEnabled);
|
||||||
|
|
||||||
|
int getVirtualizerStrength();
|
||||||
|
|
||||||
|
void setVirtualizerStrength(int strength);
|
||||||
|
|
||||||
|
boolean isRunning();
|
||||||
|
|
||||||
|
Equalizer getEqualizer();
|
||||||
|
|
||||||
|
BassBoost getBassBoost();
|
||||||
|
|
||||||
|
Virtualizer getVirtualizer();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package code.name.monkey.retromusic.interfaces;
|
||||||
|
|
||||||
|
|
||||||
|
public interface LoaderIds {
|
||||||
|
int FOLDERS_FRAGMENT = 5;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package code.name.monkey.retromusic.interfaces;
|
||||||
|
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 14/08/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface MainActivityFragmentCallbacks {
|
||||||
|
boolean handleBackPress();
|
||||||
|
|
||||||
|
//void selectedFragment(Fragment fragment);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package code.name.monkey.retromusic.interfaces;
|
||||||
|
|
||||||
|
|
||||||
|
public interface MusicServiceEventListener {
|
||||||
|
void onServiceConnected();
|
||||||
|
|
||||||
|
void onServiceDisconnected();
|
||||||
|
|
||||||
|
void onQueueChanged();
|
||||||
|
|
||||||
|
void onPlayingMetaChanged();
|
||||||
|
|
||||||
|
void onPlayStateChanged();
|
||||||
|
|
||||||
|
void onRepeatModeChanged();
|
||||||
|
|
||||||
|
void onShuffleModeChanged();
|
||||||
|
|
||||||
|
void onMediaStoreChanged();
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package code.name.monkey.retromusic.interfaces;
|
||||||
|
|
||||||
|
import android.support.annotation.ColorInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aidan Follestad (afollestad)
|
||||||
|
*/
|
||||||
|
public interface PaletteColorHolder {
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
int getPaletteColor();
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.MediaStore.Audio.AudioColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import code.name.monkey.retromusic.model.Album;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 11/08/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class AlbumLoader {
|
||||||
|
|
||||||
|
public static Observable<ArrayList<Album>> getAllAlbums(@NonNull Context context) {
|
||||||
|
Observable<ArrayList<Song>> songs = SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
getSongLoaderSortOrder(context))
|
||||||
|
);
|
||||||
|
|
||||||
|
return splitIntoAlbums(songs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Album>> getAlbums(@NonNull final Context context,
|
||||||
|
String query) {
|
||||||
|
Observable<ArrayList<Song>> songs = SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||||
|
context,
|
||||||
|
AudioColumns.ALBUM + " LIKE ?",
|
||||||
|
new String[]{"%" + query + "%"},
|
||||||
|
getSongLoaderSortOrder(context))
|
||||||
|
);
|
||||||
|
return splitIntoAlbums(songs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<Album> getAlbum(@NonNull final Context context, int albumId) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
Observable<ArrayList<Song>> songs = SongLoader.getSongs(SongLoader
|
||||||
|
.makeSongCursor(context, AudioColumns.ALBUM_ID + "=?",
|
||||||
|
new String[]{String.valueOf(albumId)}, getSongLoaderSortOrder(context)));
|
||||||
|
songs.subscribe(songs1 -> {
|
||||||
|
e.onNext(new Album(songs1));
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Album>> splitIntoAlbums(
|
||||||
|
@Nullable final Observable<ArrayList<Song>> songs) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
ArrayList<Album> albums = new ArrayList<>();
|
||||||
|
if (songs != null) {
|
||||||
|
songs.subscribe(songs1 -> {
|
||||||
|
for (Song song : songs1) {
|
||||||
|
getOrCreateAlbum(albums, song.albumId).subscribe(album -> album.songs.add(song));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
e.onNext(albums);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ArrayList<Album> splitIntoAlbums(@Nullable final ArrayList<Song> songs) {
|
||||||
|
ArrayList<Album> albums = new ArrayList<>();
|
||||||
|
if (songs != null) {
|
||||||
|
for (Song song : songs) {
|
||||||
|
getOrCreateAlbum(albums, song.albumId).subscribe(album -> album.songs.add(song));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return albums;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Observable<Album> getOrCreateAlbum(ArrayList<Album> albums, int albumId) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
for (Album album : albums) {
|
||||||
|
if (!album.songs.isEmpty() && album.songs.get(0).albumId == albumId) {
|
||||||
|
e.onNext(album);
|
||||||
|
e.onComplete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Album album = new Album();
|
||||||
|
albums.add(album);
|
||||||
|
e.onNext(album);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSongLoaderSortOrder(Context context) {
|
||||||
|
return PreferenceUtil.getInstance(context).getAlbumSortOrder() + ", " +
|
||||||
|
//PreferenceUtil.getInstance(context).getAlbumSongSortOrder() + "," +
|
||||||
|
PreferenceUtil.getInstance(context).getAlbumDetailSongSortOrder();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.MediaStore.Audio.AudioColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Album;
|
||||||
|
import code.name.monkey.retromusic.model.Artist;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
public class ArtistLoader {
|
||||||
|
public static String getSongLoaderSortOrder(Context context) {
|
||||||
|
return PreferenceUtil.getInstance(context).getArtistSortOrder() + ", " +
|
||||||
|
PreferenceUtil.getInstance(context).getArtistAlbumSortOrder() + ", " +
|
||||||
|
PreferenceUtil.getInstance(context).getAlbumDetailSongSortOrder() + ", " +
|
||||||
|
PreferenceUtil.getInstance(context).getArtistDetailSongSortOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<Artist> getArtist(@NonNull final Context context, int artistId) {
|
||||||
|
return Observable.create(e -> SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||||
|
context,
|
||||||
|
AudioColumns.ARTIST_ID + "=?",
|
||||||
|
new String[]{String.valueOf(artistId)},
|
||||||
|
getSongLoaderSortOrder(context)))
|
||||||
|
.subscribe(songs -> {
|
||||||
|
Artist artist = new Artist(AlbumLoader.splitIntoAlbums(songs));
|
||||||
|
e.onNext(artist);
|
||||||
|
e.onComplete();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Artist>> getAllArtists(@NonNull final Context context) {
|
||||||
|
return Observable.create(e -> SongLoader
|
||||||
|
.getSongs(SongLoader.makeSongCursor(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
getSongLoaderSortOrder(context))
|
||||||
|
).subscribe(songs -> {
|
||||||
|
e.onNext(splitIntoArtists(AlbumLoader.splitIntoAlbums(songs)));
|
||||||
|
e.onComplete();
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Artist>> getArtists(@NonNull final Context context, String query) {
|
||||||
|
return Observable.create(e -> SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||||
|
context,
|
||||||
|
AudioColumns.ARTIST + " LIKE ?",
|
||||||
|
new String[]{"%" + query + "%"},
|
||||||
|
getSongLoaderSortOrder(context))
|
||||||
|
).subscribe(songs -> {
|
||||||
|
e.onNext(splitIntoArtists(AlbumLoader.splitIntoAlbums(songs)));
|
||||||
|
e.onComplete();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ArrayList<Artist> splitIntoArtists(@Nullable final ArrayList<Album> albums) {
|
||||||
|
ArrayList<Artist> artists = new ArrayList<>();
|
||||||
|
if (albums != null) {
|
||||||
|
for (Album album : albums) {
|
||||||
|
getOrCreateArtist(artists, album.getArtistId()).albums.add(album);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return artists;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Artist getOrCreateArtist(ArrayList<Artist> artists, int artistId) {
|
||||||
|
for (Artist artist : artists) {
|
||||||
|
if (!artist.albums.isEmpty() && !artist.albums.get(0).songs.isEmpty() && artist.albums.get(0).songs.get(0).artistId == artistId) {
|
||||||
|
return artist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Artist album = new Artist();
|
||||||
|
artists.add(album);
|
||||||
|
return album;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Observable<ArrayList<Artist>> splitIntoArtists(Observable<ArrayList<Album>> albums) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
ArrayList<Artist> artists = new ArrayList<>();
|
||||||
|
albums.subscribe(localAlbums -> {
|
||||||
|
if (localAlbums != null) {
|
||||||
|
for (Album album : localAlbums) {
|
||||||
|
getOrCreateArtist(artists, album.getArtistId()).albums.add(album);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.onNext(artists);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public static Observable<ArrayList<Artist>> getAllArtists(Context context) {
|
||||||
|
return getArtistsForCursor(makeArtistCursor(context, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Observable<Artist> getArtist(Context context, long id) {
|
||||||
|
return getArtist(makeArtistCursor(context, "_id=?", new String[]{String.valueOf(id)}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Observable<ArrayList<Artist>> getArtists(Context context, String paramString) {
|
||||||
|
return getArtistsForCursor(makeArtistCursor(context, "artist LIKE ?", new String[]{"%" + paramString + "%"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Cursor makeArtistCursor(Context context, String selection, String[] paramArrayOfString) {
|
||||||
|
final String artistSortOrder = PreferenceUtil.getInstance(context).getArtistSortOrder();
|
||||||
|
Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, new String[]{"_id", "artist", "number_of_albums", "number_of_tracks"}, selection, paramArrayOfString, artistSortOrder);
|
||||||
|
return cursor;
|
||||||
|
}*/
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
|
||||||
|
public class ArtistSongLoader extends SongLoader {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getArtistSongList(@NonNull final Context context, final int artistId) {
|
||||||
|
return getSongs(makeArtistSongCursor(context, artistId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Cursor makeArtistSongCursor(@NonNull final Context context, final int artistId) {
|
||||||
|
try {
|
||||||
|
return makeSongCursor(
|
||||||
|
context,
|
||||||
|
MediaStore.Audio.AudioColumns.ARTIST_ID + "=?",
|
||||||
|
new String[]{
|
||||||
|
String.valueOf(artistId)
|
||||||
|
},
|
||||||
|
PreferenceUtil.getInstance(context).getArtistSongSortOrder()
|
||||||
|
);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.provider.MediaStore.Audio.Genres;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Genre;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
public class GenreLoader {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Genre>> getAllGenres(@NonNull final Context context) {
|
||||||
|
return getGenresFromCursor(context, makeGenreCursor(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getSongs(@NonNull final Context context, final int genreId) {
|
||||||
|
// The genres table only stores songs that have a genre specified,
|
||||||
|
// so we need to get songs without a genre a different way.
|
||||||
|
if (genreId == -1) {
|
||||||
|
return getSongsWithNoGenre(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SongLoader.getSongs(makeGenreSongCursor(context, genreId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Genre getGenreFromCursor(@NonNull Context context, @NonNull final Cursor cursor) {
|
||||||
|
final int id = cursor.getInt(0);
|
||||||
|
final String name = cursor.getString(1);
|
||||||
|
final int songCount = getSongs(context, id).blockingFirst().size();
|
||||||
|
return new Genre(id, name, songCount);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Observable<ArrayList<Song>> getSongsWithNoGenre(@NonNull final Context context) {
|
||||||
|
String selection = BaseColumns._ID + " NOT IN " +
|
||||||
|
"(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)";
|
||||||
|
return SongLoader.getSongs(SongLoader.makeSongCursor(context, selection, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasSongsWithNoGenre(@NonNull final Context context) {
|
||||||
|
final Cursor allSongsCursor = SongLoader.makeSongCursor(context, null, null);
|
||||||
|
final Cursor allSongsWithGenreCursor = makeAllSongsWithGenreCursor(context);
|
||||||
|
|
||||||
|
if (allSongsCursor == null || allSongsWithGenreCursor == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean hasSongsWithNoGenre = allSongsCursor.getCount() > allSongsWithGenreCursor.getCount();
|
||||||
|
allSongsCursor.close();
|
||||||
|
allSongsWithGenreCursor.close();
|
||||||
|
return hasSongsWithNoGenre;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Cursor makeAllSongsWithGenreCursor(@NonNull final Context context) {
|
||||||
|
try {
|
||||||
|
return context.getContentResolver().query(
|
||||||
|
Uri.parse("content://media/external/audio/genres/all/members"),
|
||||||
|
new String[]{Genres.Members.AUDIO_ID}, null, null, null);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Cursor makeGenreSongCursor(@NonNull final Context context, int genreId) {
|
||||||
|
try {
|
||||||
|
return context.getContentResolver().query(
|
||||||
|
Genres.Members.getContentUri("external", genreId),
|
||||||
|
SongLoader.BASE_PROJECTION, SongLoader.BASE_SELECTION, null, PreferenceUtil.getInstance(context).getSongSortOrder());
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Observable<ArrayList<Genre>> getGenresFromCursor(@NonNull final Context context, @Nullable final Cursor cursor) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
final ArrayList<Genre> genres = new ArrayList<>();
|
||||||
|
if (cursor != null) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
Genre genre = getGenreFromCursor(context, cursor);
|
||||||
|
if (genre.songCount > 0) {
|
||||||
|
genres.add(genre);
|
||||||
|
} else {
|
||||||
|
// try to remove the empty genre from the media store
|
||||||
|
try {
|
||||||
|
context.getContentResolver().delete(Genres.EXTERNAL_CONTENT_URI, Genres._ID + " == " + genre.id, null);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
// nothing we can do then
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
e.onNext(genres);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Cursor makeGenreCursor(@NonNull final Context context) {
|
||||||
|
final String[] projection = new String[]{
|
||||||
|
Genres._ID,
|
||||||
|
Genres.NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
return context.getContentResolver().query(
|
||||||
|
Genres.EXTERNAL_CONTENT_URI,
|
||||||
|
projection, null, null, PreferenceUtil.getInstance(context).getGenreSortOrder());
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.provider.MediaStore.Audio.AudioColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Hemanth S (h4h13).
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class GenreSongsLoader {
|
||||||
|
|
||||||
|
public static Observable<ArrayList<Song>> getGenreSongsList(@NonNull Context context, @NonNull int genreId) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
ArrayList<Song> list = new ArrayList<>();
|
||||||
|
Cursor cursor = makeGenreSongCursor(context, genreId);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
list.add(getGenreSongFromCursorImpl(cursor));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
e.onNext((ArrayList<Song>) (List) list);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Song getGenreSongFromCursorImpl(@NonNull Cursor cursor) {
|
||||||
|
final int id = cursor.getInt(0);
|
||||||
|
final String title = cursor.getString(1);
|
||||||
|
final int trackNumber = cursor.getInt(2);
|
||||||
|
final int year = cursor.getInt(3);
|
||||||
|
final long duration = cursor.getLong(4);
|
||||||
|
final String data = cursor.getString(5);
|
||||||
|
final int dateModified = cursor.getInt(6);
|
||||||
|
final int albumId = cursor.getInt(7);
|
||||||
|
final String albumName = cursor.getString(8);
|
||||||
|
final int artistId = cursor.getInt(9);
|
||||||
|
final String artistName = cursor.getString(10);
|
||||||
|
|
||||||
|
return new Song(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Cursor makeGenreSongCursor(Context context, long genreId) {
|
||||||
|
try {
|
||||||
|
return context.getContentResolver().query(
|
||||||
|
MediaStore.Audio.Genres.Members.getContentUri("external", genreId),
|
||||||
|
new String[]{
|
||||||
|
MediaStore.Audio.Playlists.Members.AUDIO_ID,// 0
|
||||||
|
AudioColumns.TITLE,// 1
|
||||||
|
AudioColumns.TRACK,// 2
|
||||||
|
AudioColumns.YEAR,// 3
|
||||||
|
AudioColumns.DURATION,// 4
|
||||||
|
AudioColumns.DATA,// 5
|
||||||
|
AudioColumns.DATE_MODIFIED,// 6
|
||||||
|
AudioColumns.ALBUM_ID,// 7
|
||||||
|
AudioColumns.ALBUM,// 8
|
||||||
|
AudioColumns.ARTIST_ID,// 9
|
||||||
|
AudioColumns.ARTIST,// 10
|
||||||
|
}, SongLoader.BASE_SELECTION, null,
|
||||||
|
MediaStore.Audio.Genres.Members.DEFAULT_SORT_ORDER);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist;
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.HistoryPlaylist;
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist;
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class HomeLoader {
|
||||||
|
|
||||||
|
public static Observable<ArrayList<AbsSmartPlaylist>> getRecentAndTopThings(
|
||||||
|
@NonNull Context context) {
|
||||||
|
ArrayList<AbsSmartPlaylist> objects = new ArrayList<>();
|
||||||
|
return Observable.create(e -> {
|
||||||
|
|
||||||
|
new HistoryPlaylist(context).getSongs(context).subscribe(songs -> {
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
objects.add(new HistoryPlaylist(context));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
new LastAddedPlaylist(context).getSongs(context).subscribe(songs -> {
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
objects.add(new LastAddedPlaylist(context));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
new MyTopTracksPlaylist(context).getSongs(context).subscribe(songs -> {
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
objects.add(new MyTopTracksPlaylist(context));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
e.onNext(objects);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Observable<ArrayList<Playlist>> getHomeLoader(@NonNull Context context) {
|
||||||
|
ArrayList<Playlist> playlists = new ArrayList<>();
|
||||||
|
PlaylistLoader.getAllPlaylists(context)
|
||||||
|
.subscribe(playlists1 -> {
|
||||||
|
if (playlists1.size() > 0) {
|
||||||
|
for (Playlist playlist : playlists1) {
|
||||||
|
PlaylistSongsLoader.getPlaylistSongList(context, playlist)
|
||||||
|
.subscribe(songs -> {
|
||||||
|
if (songs.size() > 0) {
|
||||||
|
playlists.add(playlist);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Observable.just(playlists);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Album;
|
||||||
|
import code.name.monkey.retromusic.model.Artist;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.annotations.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 16/08/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class LastAddedSongsLoader {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getLastAddedSongs(@NonNull Context context) {
|
||||||
|
return SongLoader.getSongs(makeLastAddedCursor(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Cursor makeLastAddedCursor(@NonNull final Context context) {
|
||||||
|
long cutoff = PreferenceUtil.getInstance(context).getLastAddedCutoff();
|
||||||
|
|
||||||
|
return SongLoader.makeSongCursor(
|
||||||
|
context,
|
||||||
|
MediaStore.Audio.Media.DATE_ADDED + ">?",
|
||||||
|
new String[]{String.valueOf(cutoff)},
|
||||||
|
MediaStore.Audio.Media.DATE_ADDED + " DESC");
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Album>> getLastAddedAlbums(@NonNull Context context) {
|
||||||
|
return AlbumLoader.splitIntoAlbums(getLastAddedSongs(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Artist>> getLastAddedArtists(@NonNull Context context) {
|
||||||
|
return ArtistLoader.splitIntoArtists(getLastAddedAlbums(context));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.provider.MediaStore.Audio.PlaylistsColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 16/08/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PlaylistLoader {
|
||||||
|
@Nullable
|
||||||
|
public static Cursor makePlaylistCursor(@NonNull final Context context, final String selection, final String[] values) {
|
||||||
|
try {
|
||||||
|
return context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
|
||||||
|
new String[]{
|
||||||
|
/* 0 */
|
||||||
|
BaseColumns._ID,
|
||||||
|
/* 1 */
|
||||||
|
PlaylistsColumns.NAME
|
||||||
|
}, selection, values, MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<Playlist> getPlaylist(@Nullable final Cursor cursor) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
Playlist playlist = new Playlist();
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
playlist = getPlaylistFromCursorImpl(cursor);
|
||||||
|
}
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
|
||||||
|
e.onNext(playlist);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<Playlist> getPlaylist(@NonNull final Context context, final String playlistName) {
|
||||||
|
return getPlaylist(makePlaylistCursor(
|
||||||
|
context,
|
||||||
|
PlaylistsColumns.NAME + "=?",
|
||||||
|
new String[]{
|
||||||
|
playlistName
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<Playlist> getPlaylist(@NonNull final Context context, final int playlistId) {
|
||||||
|
return getPlaylist(makePlaylistCursor(
|
||||||
|
context,
|
||||||
|
BaseColumns._ID + "=?",
|
||||||
|
new String[]{
|
||||||
|
String.valueOf(playlistId)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Playlist getPlaylistFromCursorImpl(@NonNull final Cursor cursor) {
|
||||||
|
|
||||||
|
final int id = cursor.getInt(0);
|
||||||
|
final String name = cursor.getString(1);
|
||||||
|
return new Playlist(id, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Playlist>> getAllPlaylists(@Nullable final Cursor cursor) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
ArrayList<Playlist> playlists = new ArrayList<>();
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
playlists.add(getPlaylistFromCursorImpl(cursor));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
|
||||||
|
e.onNext(playlists);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Playlist>> getAllPlaylists(@NonNull final Context context) {
|
||||||
|
return getAllPlaylists(makePlaylistCursor(context, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deletePlaylists(Context context, long playlistId) {
|
||||||
|
Uri localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
|
||||||
|
StringBuilder localStringBuilder = new StringBuilder();
|
||||||
|
localStringBuilder.append("_id IN (");
|
||||||
|
localStringBuilder.append((playlistId));
|
||||||
|
localStringBuilder.append(")");
|
||||||
|
context.getContentResolver().delete(localUri, localStringBuilder.toString(), null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.provider.MediaStore.Audio.AudioColumns;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.model.AbsCustomPlaylist;
|
||||||
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
|
import code.name.monkey.retromusic.model.PlaylistSong;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.annotations.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 16/08/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PlaylistSongsLoader {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getPlaylistSongList(@NonNull Context context, Playlist playlist) {
|
||||||
|
if (playlist instanceof AbsCustomPlaylist) {
|
||||||
|
return ((AbsCustomPlaylist) playlist).getSongs(context);
|
||||||
|
} else {
|
||||||
|
//noinspection unchecked
|
||||||
|
return getPlaylistSongList(context, playlist.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getPlaylistSongList(@NonNull Context context, final int playlistId) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
ArrayList<PlaylistSong> songs = new ArrayList<>();
|
||||||
|
Cursor cursor = makePlaylistSongCursor(context, playlistId);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
e.onNext((ArrayList<Song>) (List) songs);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static PlaylistSong getPlaylistSongFromCursorImpl(@NonNull Cursor cursor, int playlistId) {
|
||||||
|
final int id = cursor.getInt(0);
|
||||||
|
final String title = cursor.getString(1);
|
||||||
|
final int trackNumber = cursor.getInt(2);
|
||||||
|
final int year = cursor.getInt(3);
|
||||||
|
final long duration = cursor.getLong(4);
|
||||||
|
final String data = cursor.getString(5);
|
||||||
|
final int dateModified = cursor.getInt(6);
|
||||||
|
final int albumId = cursor.getInt(7);
|
||||||
|
final String albumName = cursor.getString(8);
|
||||||
|
final int artistId = cursor.getInt(9);
|
||||||
|
final String artistName = cursor.getString(10);
|
||||||
|
final int idInPlaylist = cursor.getInt(11);
|
||||||
|
|
||||||
|
return new PlaylistSong(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, playlistId, idInPlaylist);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Cursor makePlaylistSongCursor(@NonNull final Context context, final int playlistId) {
|
||||||
|
try {
|
||||||
|
return context.getContentResolver().query(
|
||||||
|
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
|
||||||
|
new String[]{
|
||||||
|
MediaStore.Audio.Playlists.Members.AUDIO_ID,// 0
|
||||||
|
AudioColumns.TITLE,// 1
|
||||||
|
AudioColumns.TRACK,// 2
|
||||||
|
AudioColumns.YEAR,// 3
|
||||||
|
AudioColumns.DURATION,// 4
|
||||||
|
AudioColumns.DATA,// 5
|
||||||
|
AudioColumns.DATE_MODIFIED,// 6
|
||||||
|
AudioColumns.ALBUM_ID,// 7
|
||||||
|
AudioColumns.ALBUM,// 8
|
||||||
|
AudioColumns.ARTIST_ID,// 9
|
||||||
|
AudioColumns.ARTIST,// 10
|
||||||
|
MediaStore.Audio.Playlists.Members._ID // 11
|
||||||
|
}, SongLoader.BASE_SELECTION, null,
|
||||||
|
MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
public class SearchLoader {
|
||||||
|
|
||||||
|
public static Observable<ArrayList<Object>> searchAll(@NonNull Context context, @NonNull String query) {
|
||||||
|
ArrayList<Object> results = new ArrayList<>();
|
||||||
|
return Observable.create(e -> {
|
||||||
|
if (!TextUtils.isEmpty(query)) {
|
||||||
|
SongLoader.getSongs(context, query)
|
||||||
|
.subscribe(songs -> {
|
||||||
|
if (!songs.isEmpty()) {
|
||||||
|
results.add(context.getResources().getString(R.string.songs));
|
||||||
|
results.addAll(songs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ArtistLoader.getArtists(context, query)
|
||||||
|
.subscribe(artists -> {
|
||||||
|
if (!artists.isEmpty()) {
|
||||||
|
results.add(context.getResources().getString(R.string.artists));
|
||||||
|
results.addAll(artists);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AlbumLoader.getAlbums(context, query)
|
||||||
|
.subscribe(albums -> {
|
||||||
|
if (!albums.isEmpty()) {
|
||||||
|
results.add(context.getResources().getString(R.string.albums));
|
||||||
|
results.addAll(albums);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
e.onNext(results);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.provider.MediaStore.Audio.AudioColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.helper.ShuffleHelper;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.providers.BlacklistStore;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 10/08/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class SongLoader {
|
||||||
|
|
||||||
|
protected static final String BASE_SELECTION =
|
||||||
|
AudioColumns.IS_MUSIC + "=1" + " AND " + AudioColumns.TITLE + " != ''";
|
||||||
|
protected static final String[] BASE_PROJECTION = new String[]{
|
||||||
|
BaseColumns._ID,// 0
|
||||||
|
AudioColumns.TITLE,// 1
|
||||||
|
AudioColumns.TRACK,// 2
|
||||||
|
AudioColumns.YEAR,// 3
|
||||||
|
AudioColumns.DURATION,// 4
|
||||||
|
AudioColumns.DATA,// 5
|
||||||
|
AudioColumns.DATE_MODIFIED,// 6
|
||||||
|
AudioColumns.ALBUM_ID,// 7
|
||||||
|
AudioColumns.ALBUM,// 8
|
||||||
|
AudioColumns.ARTIST_ID,// 9
|
||||||
|
AudioColumns.ARTIST,// 10
|
||||||
|
};
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getAllSongs(@NonNull Context context) {
|
||||||
|
Cursor cursor = makeSongCursor(context, null, null);
|
||||||
|
return getSongs(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getSongs(@NonNull final Context context,
|
||||||
|
final String query) {
|
||||||
|
Cursor cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?",
|
||||||
|
new String[]{"%" + query + "%"});
|
||||||
|
return getSongs(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getSongs(@Nullable final Cursor cursor) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
ArrayList<Song> songs = new ArrayList<>();
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
songs.add(getSongFromCursorImpl(cursor));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
e.onNext(songs);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Song getSongFromCursorImpl(@NonNull Cursor cursor) {
|
||||||
|
final int id = cursor.getInt(0);
|
||||||
|
final String title = cursor.getString(1);
|
||||||
|
final int trackNumber = cursor.getInt(2);
|
||||||
|
final int year = cursor.getInt(3);
|
||||||
|
final long duration = cursor.getLong(4);
|
||||||
|
final String data = cursor.getString(5);
|
||||||
|
final long dateModified = cursor.getLong(6);
|
||||||
|
final int albumId = cursor.getInt(7);
|
||||||
|
final String albumName = cursor.getString(8);
|
||||||
|
final int artistId = cursor.getInt(9);
|
||||||
|
final String artistName = cursor.getString(10);
|
||||||
|
|
||||||
|
return new Song(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName,
|
||||||
|
artistId, artistName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Cursor makeSongCursor(@NonNull final Context context,
|
||||||
|
@Nullable final String selection, final String[] selectionValues) {
|
||||||
|
return makeSongCursor(context, selection, selectionValues,
|
||||||
|
PreferenceUtil.getInstance(context).getSongSortOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Cursor makeSongCursor(@NonNull final Context context, @Nullable String selection,
|
||||||
|
String[] selectionValues, final String sortOrder) {
|
||||||
|
if (selection != null && !selection.trim().equals("")) {
|
||||||
|
selection = BASE_SELECTION + " AND " + selection;
|
||||||
|
} else {
|
||||||
|
selection = BASE_SELECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blacklist
|
||||||
|
ArrayList<String> paths = BlacklistStore.getInstance(context).getPaths();
|
||||||
|
if (!paths.isEmpty()) {
|
||||||
|
selection = generateBlacklistSelection(selection, paths.size());
|
||||||
|
selectionValues = addBlacklistSelectionValues(selectionValues, paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
BASE_PROJECTION, selection, selectionValues, sortOrder);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateBlacklistSelection(String selection, int pathCount) {
|
||||||
|
StringBuilder newSelection = new StringBuilder(
|
||||||
|
selection != null && !selection.trim().equals("") ? selection + " AND " : "");
|
||||||
|
newSelection.append(AudioColumns.DATA + " NOT LIKE ?");
|
||||||
|
for (int i = 0; i < pathCount - 1; i++) {
|
||||||
|
newSelection.append(" AND " + AudioColumns.DATA + " NOT LIKE ?");
|
||||||
|
}
|
||||||
|
return newSelection.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] addBlacklistSelectionValues(String[] selectionValues,
|
||||||
|
ArrayList<String> paths) {
|
||||||
|
if (selectionValues == null) {
|
||||||
|
selectionValues = new String[0];
|
||||||
|
}
|
||||||
|
String[] newSelectionValues = new String[selectionValues.length + paths.size()];
|
||||||
|
System.arraycopy(selectionValues, 0, newSelectionValues, 0, selectionValues.length);
|
||||||
|
for (int i = selectionValues.length; i < newSelectionValues.length; i++) {
|
||||||
|
newSelectionValues[i] = paths.get(i - selectionValues.length) + "%";
|
||||||
|
}
|
||||||
|
return newSelectionValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<Song> getSong(@Nullable Cursor cursor) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
Song song;
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
song = getSongFromCursorImpl(cursor);
|
||||||
|
} else {
|
||||||
|
song = Song.EMPTY_SONG;
|
||||||
|
}
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
e.onNext(song);
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<Song> getSong(@NonNull final Context context, final int queryId) {
|
||||||
|
Cursor cursor = makeSongCursor(context, AudioColumns._ID + "=?",
|
||||||
|
new String[]{String.valueOf(queryId)});
|
||||||
|
return getSong(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Observable<ArrayList<Song>> suggestSongs(@NonNull Context context) {
|
||||||
|
return Observable.create(observer -> {
|
||||||
|
SongLoader.getAllSongs(context)
|
||||||
|
.subscribe(songs -> {
|
||||||
|
ArrayList<Song> list = new ArrayList<>();
|
||||||
|
if (songs.isEmpty()) {
|
||||||
|
observer.onNext(new ArrayList<>());
|
||||||
|
observer.onComplete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ShuffleHelper.makeShuffleList(songs, -1);
|
||||||
|
if (songs.size() > 10) {
|
||||||
|
list.addAll(songs.subList(0, 10));
|
||||||
|
} else {
|
||||||
|
list.addAll(songs);
|
||||||
|
}
|
||||||
|
observer.onNext(list);
|
||||||
|
observer.onComplete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The CyanogenMod Project
|
||||||
|
*
|
||||||
|
* 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 code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.database.AbstractCursor;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This cursor basically wraps a song cursor and is given a list of the order of the ids of the
|
||||||
|
* contents of the cursor. It wraps the Cursor and simulates the internal cursor being sorted
|
||||||
|
* by moving the point to the appropriate spot
|
||||||
|
*/
|
||||||
|
public class SortedCursor extends AbstractCursor {
|
||||||
|
// cursor to wrap
|
||||||
|
private final Cursor mCursor;
|
||||||
|
// the map of external indices to internal indices
|
||||||
|
private ArrayList<Integer> mOrderedPositions;
|
||||||
|
// this contains the ids that weren't found in the underlying cursor
|
||||||
|
private ArrayList<String> mMissingValues;
|
||||||
|
// this contains the mapped cursor positions and afterwards the extra ids that weren't found
|
||||||
|
private HashMap<String, Integer> mMapCursorPositions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cursor to wrap
|
||||||
|
* @param order the list of unique ids in sorted order to display
|
||||||
|
* @param columnName the column name of the id to look up in the internal cursor
|
||||||
|
*/
|
||||||
|
public SortedCursor(@NonNull final Cursor cursor, @Nullable final String[] order, final String columnName) {
|
||||||
|
mCursor = cursor;
|
||||||
|
mMissingValues = buildCursorPositionMapping(order, columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function populates mOrderedPositions with the cursor positions in the order based
|
||||||
|
* on the order passed in
|
||||||
|
*
|
||||||
|
* @param order the target order of the internal cursor
|
||||||
|
* @return returns the ids that aren't found in the underlying cursor
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
private ArrayList<String> buildCursorPositionMapping(@Nullable final String[] order, final String columnName) {
|
||||||
|
ArrayList<String> missingValues = new ArrayList<>();
|
||||||
|
|
||||||
|
mOrderedPositions = new ArrayList<>(mCursor.getCount());
|
||||||
|
|
||||||
|
mMapCursorPositions = new HashMap<>(mCursor.getCount());
|
||||||
|
final int valueColumnIndex = mCursor.getColumnIndex(columnName);
|
||||||
|
|
||||||
|
if (mCursor.moveToFirst()) {
|
||||||
|
// first figure out where each of the ids are in the cursor
|
||||||
|
do {
|
||||||
|
mMapCursorPositions.put(mCursor.getString(valueColumnIndex), mCursor.getPosition());
|
||||||
|
} while (mCursor.moveToNext());
|
||||||
|
|
||||||
|
if (order != null) {
|
||||||
|
// now create the ordered positions to map to the internal cursor given the
|
||||||
|
// external sort order
|
||||||
|
for (final String value : order) {
|
||||||
|
if (mMapCursorPositions.containsKey(value)) {
|
||||||
|
mOrderedPositions.add(mMapCursorPositions.get(value));
|
||||||
|
mMapCursorPositions.remove(value);
|
||||||
|
} else {
|
||||||
|
missingValues.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mCursor.moveToFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
return missingValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the list of ids that weren't found in the underlying cursor
|
||||||
|
*/
|
||||||
|
public ArrayList<String> getMissingValues() {
|
||||||
|
return mMissingValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the list of ids that were in the underlying cursor but not part of the ordered list
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Collection<String> getExtraValues() {
|
||||||
|
return mMapCursorPositions.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mCursor.close();
|
||||||
|
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return mOrderedPositions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getColumnNames() {
|
||||||
|
return mCursor.getColumnNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString(int column) {
|
||||||
|
return mCursor.getString(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short getShort(int column) {
|
||||||
|
return mCursor.getShort(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInt(int column) {
|
||||||
|
return mCursor.getInt(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLong(int column) {
|
||||||
|
return mCursor.getLong(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getFloat(int column) {
|
||||||
|
return mCursor.getFloat(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getDouble(int column) {
|
||||||
|
return mCursor.getDouble(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNull(int column) {
|
||||||
|
return mCursor.isNull(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(int oldPosition, int newPosition) {
|
||||||
|
if (newPosition >= 0 && newPosition < getCount()) {
|
||||||
|
mCursor.moveToPosition(mOrderedPositions.get(newPosition));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The CyanogenMod Project
|
||||||
|
*
|
||||||
|
* 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 code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.database.AbstractCursor;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This cursor basically wraps a song cursor and is given a list of the order of the ids of the
|
||||||
|
* contents of the cursor. It wraps the Cursor and simulates the internal cursor being sorted
|
||||||
|
* by moving the point to the appropriate spot
|
||||||
|
*/
|
||||||
|
public class SortedLongCursor extends AbstractCursor {
|
||||||
|
// cursor to wrap
|
||||||
|
private final Cursor mCursor;
|
||||||
|
// the map of external indices to internal indices
|
||||||
|
private ArrayList<Integer> mOrderedPositions;
|
||||||
|
// this contains the ids that weren't found in the underlying cursor
|
||||||
|
private ArrayList<Long> mMissingIds;
|
||||||
|
// this contains the mapped cursor positions and afterwards the extra ids that weren't found
|
||||||
|
private HashMap<Long, Integer> mMapCursorPositions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cursor to wrap
|
||||||
|
* @param order the list of unique ids in sorted order to display
|
||||||
|
* @param columnName the column name of the id to look up in the internal cursor
|
||||||
|
*/
|
||||||
|
public SortedLongCursor(final Cursor cursor, final long[] order, final String columnName) {
|
||||||
|
|
||||||
|
mCursor = cursor;
|
||||||
|
mMissingIds = buildCursorPositionMapping(order, columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function populates mOrderedPositions with the cursor positions in the order based
|
||||||
|
* on the order passed in
|
||||||
|
*
|
||||||
|
* @param order the target order of the internal cursor
|
||||||
|
* @return returns the ids that aren't found in the underlying cursor
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
private ArrayList<Long> buildCursorPositionMapping(@Nullable final long[] order, final String columnName) {
|
||||||
|
ArrayList<Long> missingIds = new ArrayList<>();
|
||||||
|
|
||||||
|
mOrderedPositions = new ArrayList<>(mCursor.getCount());
|
||||||
|
|
||||||
|
mMapCursorPositions = new HashMap<>(mCursor.getCount());
|
||||||
|
final int idPosition = mCursor.getColumnIndex(columnName);
|
||||||
|
|
||||||
|
if (mCursor.moveToFirst()) {
|
||||||
|
// first figure out where each of the ids are in the cursor
|
||||||
|
do {
|
||||||
|
mMapCursorPositions.put(mCursor.getLong(idPosition), mCursor.getPosition());
|
||||||
|
} while (mCursor.moveToNext());
|
||||||
|
|
||||||
|
// now create the ordered positions to map to the internal cursor given the
|
||||||
|
// external sort order
|
||||||
|
for (int i = 0; order != null && i < order.length; i++) {
|
||||||
|
final long id = order[i];
|
||||||
|
if (mMapCursorPositions.containsKey(id)) {
|
||||||
|
mOrderedPositions.add(mMapCursorPositions.get(id));
|
||||||
|
mMapCursorPositions.remove(id);
|
||||||
|
} else {
|
||||||
|
missingIds.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mCursor.moveToFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
return missingIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the list of ids that weren't found in the underlying cursor
|
||||||
|
*/
|
||||||
|
public ArrayList<Long> getMissingIds() {
|
||||||
|
return mMissingIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the list of ids that were in the underlying cursor but not part of the ordered list
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Collection<Long> getExtraIds() {
|
||||||
|
return mMapCursorPositions.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mCursor.close();
|
||||||
|
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return mOrderedPositions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getColumnNames() {
|
||||||
|
return mCursor.getColumnNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString(int column) {
|
||||||
|
return mCursor.getString(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short getShort(int column) {
|
||||||
|
return mCursor.getShort(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInt(int column) {
|
||||||
|
return mCursor.getInt(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLong(int column) {
|
||||||
|
return mCursor.getLong(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getFloat(int column) {
|
||||||
|
return mCursor.getFloat(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getDouble(int column) {
|
||||||
|
return mCursor.getDouble(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNull(int column) {
|
||||||
|
return mCursor.isNull(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(int oldPosition, int newPosition) {
|
||||||
|
if (newPosition >= 0 && newPosition < getCount()) {
|
||||||
|
mCursor.moveToPosition(mOrderedPositions.get(newPosition));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
package code.name.monkey.retromusic.loaders;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import code.name.monkey.retromusic.model.Album;
|
||||||
|
import code.name.monkey.retromusic.model.Artist;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.providers.HistoryStore;
|
||||||
|
import code.name.monkey.retromusic.providers.SongPlayCountStore;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 16/08/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class TopAndRecentlyPlayedTracksLoader {
|
||||||
|
|
||||||
|
private static final int NUMBER_OF_TOP_TRACKS = 99;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getRecentlyPlayedTracks(@NonNull Context context) {
|
||||||
|
return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Song>> getTopTracks(@NonNull Context context) {
|
||||||
|
return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Cursor makeRecentTracksCursorAndClearUpDatabase(@NonNull final Context context) {
|
||||||
|
SortedLongCursor retCursor = makeRecentTracksCursorImpl(context);
|
||||||
|
|
||||||
|
// clean up the databases with any ids not found
|
||||||
|
if (retCursor != null) {
|
||||||
|
ArrayList<Long> missingIds = retCursor.getMissingIds();
|
||||||
|
if (missingIds != null && missingIds.size() > 0) {
|
||||||
|
for (long id : missingIds) {
|
||||||
|
HistoryStore.getInstance(context).removeSongId(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Cursor makeTopTracksCursorAndClearUpDatabase(@NonNull final Context context) {
|
||||||
|
SortedLongCursor retCursor = makeTopTracksCursorImpl(context);
|
||||||
|
|
||||||
|
// clean up the databases with any ids not found
|
||||||
|
if (retCursor != null) {
|
||||||
|
ArrayList<Long> missingIds = retCursor.getMissingIds();
|
||||||
|
if (missingIds != null && missingIds.size() > 0) {
|
||||||
|
for (long id : missingIds) {
|
||||||
|
SongPlayCountStore.getInstance(context).removeItem(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static SortedLongCursor makeRecentTracksCursorImpl(@NonNull final Context context) {
|
||||||
|
// first get the top results ids from the internal database
|
||||||
|
Cursor songs = HistoryStore.getInstance(context).queryRecentIds();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return makeSortedCursor(context, songs,
|
||||||
|
songs.getColumnIndex(HistoryStore.RecentStoreColumns.ID));
|
||||||
|
} finally {
|
||||||
|
if (songs != null) {
|
||||||
|
songs.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static SortedLongCursor makeTopTracksCursorImpl(@NonNull final Context context) {
|
||||||
|
// first get the top results ids from the internal database
|
||||||
|
Cursor songs = SongPlayCountStore.getInstance(context)
|
||||||
|
.getTopPlayedResults(NUMBER_OF_TOP_TRACKS);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return makeSortedCursor(context, songs,
|
||||||
|
songs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID));
|
||||||
|
} finally {
|
||||||
|
if (songs != null) {
|
||||||
|
songs.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static SortedLongCursor makeSortedCursor(@NonNull final Context context,
|
||||||
|
@Nullable final Cursor cursor, final int idColumn) {
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
// create the list of ids to select against
|
||||||
|
StringBuilder selection = new StringBuilder();
|
||||||
|
selection.append(BaseColumns._ID);
|
||||||
|
selection.append(" IN (");
|
||||||
|
|
||||||
|
// this tracks the order of the ids
|
||||||
|
long[] order = new long[cursor.getCount()];
|
||||||
|
|
||||||
|
long id = cursor.getLong(idColumn);
|
||||||
|
selection.append(id);
|
||||||
|
order[cursor.getPosition()] = id;
|
||||||
|
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
selection.append(",");
|
||||||
|
|
||||||
|
id = cursor.getLong(idColumn);
|
||||||
|
order[cursor.getPosition()] = id;
|
||||||
|
selection.append(String.valueOf(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
selection.append(")");
|
||||||
|
|
||||||
|
// get a list of songs with the data given the selection statement
|
||||||
|
Cursor songCursor = SongLoader.makeSongCursor(context, selection.toString(), null);
|
||||||
|
if (songCursor != null) {
|
||||||
|
// now return the wrapped TopTracksCursor to handle sorting given order
|
||||||
|
return new SortedLongCursor(songCursor, order, BaseColumns._ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Album>> getTopAlbums(@NonNull Context context) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
getTopTracks(context).subscribe(songs -> {
|
||||||
|
if (songs.size() > 0) {
|
||||||
|
e.onNext(AlbumLoader.splitIntoAlbums(songs));
|
||||||
|
}
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Observable<ArrayList<Artist>> getTopArtists(@NonNull Context context) {
|
||||||
|
return Observable.create(e -> {
|
||||||
|
getTopAlbums(context).subscribe(albums -> {
|
||||||
|
if (albums.size() > 0) {
|
||||||
|
e.onNext(ArtistLoader.splitIntoArtists(albums));
|
||||||
|
}
|
||||||
|
e.onComplete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package code.name.monkey.retromusic.lyrics;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.rest.KogouClient;
|
||||||
|
import code.name.monkey.retromusic.rest.model.KuGouSearchLyricResult;
|
||||||
|
import code.name.monkey.retromusic.util.LyricUtil;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Hemanth S (h4h13).
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class KogouLyricsFetcher {
|
||||||
|
|
||||||
|
private KogouClient mKogouClient;
|
||||||
|
private Song mSong;
|
||||||
|
private KogouLyricsCallback mCallback;
|
||||||
|
|
||||||
|
public KogouLyricsFetcher(KogouLyricsCallback callback) {
|
||||||
|
mCallback = callback;
|
||||||
|
mKogouClient = new KogouClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadLyrics(Song song, String duration) {
|
||||||
|
mSong = song;
|
||||||
|
mKogouClient.getApiService()
|
||||||
|
.searchLyric(mSong.title, duration)
|
||||||
|
.subscribe(this::parseKugouResult,
|
||||||
|
throwable -> mCallback.onNoLyrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseKugouResult(KuGouSearchLyricResult kuGouSearchLyricResult) {
|
||||||
|
if (kuGouSearchLyricResult != null && kuGouSearchLyricResult.status == 200 &
|
||||||
|
kuGouSearchLyricResult.candidates != null &&
|
||||||
|
kuGouSearchLyricResult.candidates.size() != 0) {
|
||||||
|
KuGouSearchLyricResult.Candidates candidates = kuGouSearchLyricResult.candidates.get(0);
|
||||||
|
loadLyricsFile(candidates);
|
||||||
|
} else {
|
||||||
|
mCallback.onNoLyrics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadLyricsFile(KuGouSearchLyricResult.Candidates candidates) {
|
||||||
|
mKogouClient.getApiService().getRawLyric(candidates.id, candidates.accesskey)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe(kuGouRawLyric -> {
|
||||||
|
if (kuGouRawLyric == null) {
|
||||||
|
mCallback.onNoLyrics();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String rawLyric = LyricUtil.decryptBASE64(kuGouRawLyric.content);
|
||||||
|
LyricUtil.writeLrcToLoc(mSong.title, mSong.artistName, rawLyric);
|
||||||
|
new Handler().postDelayed(
|
||||||
|
() -> mCallback.onLyrics(LyricUtil.getLocalLyricFile(mSong.title, mSong.artistName)),
|
||||||
|
1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface KogouLyricsCallback {
|
||||||
|
|
||||||
|
void onNoLyrics();
|
||||||
|
|
||||||
|
void onLyrics(File file);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package code.name.monkey.retromusic.lyrics;
|
||||||
|
|
||||||
|
public interface LyricsEngine {
|
||||||
|
String getLyrics(String artistName, String songTitle);
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue