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