Compare commits
97 commits
Author | SHA1 | Date | |
---|---|---|---|
51c1fc4d26 | |||
e78c69bc6e | |||
75f5520759 | |||
eb7a2ed319 | |||
1c8177622d | |||
Jan Christian Grünhage | 204829ac5b | ||
Jan Christian Grünhage | 1861adfd22 | ||
Jan Christian Grünhage | 2f17819dbb | ||
Jan Christian Grünhage | 3374b3f8d0 | ||
Jan Christian Grünhage | bd7cd4d8ef | ||
Jan Christian Grünhage | 9625d9c302 | ||
Jan Christian Grünhage | f43a8f546d | ||
Jan Christian Grünhage | ac9c89ce6f | ||
Jan Christian Grünhage | a76cf7e3e4 | ||
Jan Christian Grünhage | 61991eab3e | ||
Jan Christian Grünhage | 2f7369abd8 | ||
Jan Christian Grünhage | a7e2c1de89 | ||
Jan Christian Grünhage | e016799616 | ||
Jan Christian Grünhage | 65d5dceec3 | ||
Jan Christian Grünhage | 1d8ffc81aa | ||
Jan Christian Grünhage | 0bb4a5d586 | ||
Jan Christian Grünhage | afc5d3fe2a | ||
Jan Christian Grünhage | fb620132e8 | ||
Jan Christian Grünhage | d1f0823f3c | ||
Jan Christian Grünhage | 6c7832f1e6 | ||
Jan Christian Grünhage | a0fda16193 | ||
Jan Christian Grünhage | 9ba16eba8b | ||
Jan Christian Grünhage | 6ecfb05391 | ||
9c6e32a537 | |||
Jan Christian Grünhage | 387b2e2c0a | ||
017768e88b | |||
b47c907919 | |||
587a14a67c | |||
bcf951e6d1 | |||
a5a26ee2b6 | |||
7dafd21843 | |||
Jan Christian Grünhage | 67d6fd5ffb | ||
59e90a7ab6 | |||
Jan Christian Grünhage | c33c463327 | ||
Jan Christian Grünhage | c8dd62f9c2 | ||
Jan Christian Grünhage | 1af76ee43d | ||
Jan Christian Grünhage | dc1c2018eb | ||
Jan Christian Grünhage | 353e9cdef7 | ||
Jan Christian Grünhage | c52f99fbf0 | ||
Jan Christian Grünhage | 6dcfb36482 | ||
9b4c54557b | |||
381fd5c910 | |||
259a989505 | |||
a8c51db21b | |||
Jan Christian Grünhage | c1b2473097 | ||
180fdb0e59 | |||
Jan Christian Grünhage | ef06717ae4 | ||
bbda51b845 | |||
3794434085 | |||
7502842cdd | |||
cdf640b21f | |||
2af33709ec | |||
ac32a41d69 | |||
Jan Christian Grünhage | 86e0b7c02a | ||
Jan Christian Grünhage | dc87dc7c4b | ||
Jan Christian Grünhage | b69fa49451 | ||
b503cfb966 | |||
6529c82896 | |||
81035f1082 | |||
Jan Christian Grünhage | 1cfa10bf88 | ||
782ada5c12 | |||
Jan Christian Grünhage | bb2cd0cf80 | ||
Jan Christian Grünhage | 4ba762107f | ||
Jan Christian Grünhage | 05ebba3da1 | ||
Jan Christian Grünhage | 3fc9540791 | ||
Jan Christian Grünhage | 42b72e822a | ||
Jan Christian Grünhage | 46abd0a972 | ||
Jan Christian Grünhage | db5b8b1d6c | ||
Jan Christian Grünhage | 21cbed8522 | ||
Jan Christian Grünhage | b3901dc04b | ||
Jan Christian Grünhage | e3dac5c1d3 | ||
Jan Christian Grünhage | 903743e68e | ||
Jan Christian Grünhage | f2a8a24e28 | ||
Jan Christian Grünhage | 43dd39951c | ||
Jan Christian Grünhage | 57f721b6cf | ||
Jan Christian Grünhage | 903a1162e7 | ||
Jan Christian Grünhage | 87a80b4dd7 | ||
Jan Christian Grünhage | 59ad21e0c8 | ||
Jan Christian Grünhage | 51d0ed4c7e | ||
Jan Christian Grünhage | 73cdb73b34 | ||
Jan Christian Grünhage | 8d0f23a1e5 | ||
Jan Christian Grünhage | 5eac344745 | ||
Jan Christian Grünhage | d0a5db30d6 | ||
Jan Christian Grünhage | 8cca5610ab | ||
Jan Christian Grünhage | b190d510ba | ||
Jan Christian Grünhage | 691e2a1950 | ||
Jan Christian Grünhage | 647823a104 | ||
Jan Christian Grünhage | 80c134e19d | ||
Jan Christian Grünhage | b6a445626f | ||
Jan Christian Grünhage | 470a6a2085 | ||
Jan Christian Grünhage | 2b41e5a827 | ||
Jan Christian Grünhage | aad8bdd427 |
2
.gitignore
vendored
|
@ -30,6 +30,7 @@ hs_err_pid*
|
|||
.idea/sqlDataSources.xml
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
.idea/misc.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/gradle.xml
|
||||
|
@ -107,3 +108,4 @@ captures/
|
|||
|
||||
# Keystore files
|
||||
*.jks
|
||||
gradle/wrapper/gradle-wrapper.properties
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<entry_points version="2.0" />
|
||||
</component>
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
|
||||
<OptionsSetting value="true" id="Add" />
|
||||
<OptionsSetting value="true" id="Remove" />
|
||||
<OptionsSetting value="true" id="Checkout" />
|
||||
<OptionsSetting value="true" id="Update" />
|
||||
<OptionsSetting value="true" id="Status" />
|
||||
<OptionsSetting value="true" id="Edit" />
|
||||
<ConfirmationsSetting value="0" id="Add" />
|
||||
<ConfirmationsSetting value="0" id="Remove" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,3 +1,4 @@
|
|||
Copyright (c) 2016 Jan Christian Grünhage
|
||||
Copyright (c) 2015 David Schulte
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
@ -16,4 +17,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
THE SOFTWARE.
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id="PlayMusicExporter" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" 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>
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id="PlayMusicExporterGit" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" 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>
|
56
README.md
|
@ -1,24 +1,64 @@
|
|||
# Play Music Exporter
|
||||
|
||||
This Android app exports your Play Music mp3 files directly to your sdcard using root permissions.
|
||||
This Android app exports your Play Music mp3 files directly to your sdcard
|
||||
or a documents provider using root permissions.
|
||||
You can also setup automatic export, that exports all currently cached not yet exported music.
|
||||
|
||||
[<img src="https://f-droid.org/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/app/re.jcg.playmusicexporter)
|
||||
|
||||
## About
|
||||
|
||||
This AndroidStudio project allow you to access the database from Google's PlayMusic and also allows you to export the music files as mp3 to your sdcard. There is also a nice library you can simply use in your projects.
|
||||
This AndroidStudio project allow you to access the database from Google's PlayMusic
|
||||
and also allows you to export the music files as mp3 to your sdcard or a documents provider.
|
||||
There is also a nice library you can simply use in your projects.
|
||||
|
||||
### Requirements
|
||||
|
||||
**This app and its library will require root access to your device! If your device is not rooted you can't use this app neither the lib.**
|
||||
**This app and the included library will require root access to your device!
|
||||
If your device is not rooted you can neither use this app nor the library.**
|
||||
|
||||
This project uses the [mp3agic library](https://github.com/mpatric/mp3agic) by [Michael Patricios (mpatric)](https://github.com/mpatric).
|
||||
This app uses multiple features introduced in API Level 21 (Android 5.0 Lollipop), the JobScheduler
|
||||
and the the folder selection of the storage access framework.
|
||||
If you use KitKat or lower, this app will not work.
|
||||
|
||||
### Notice
|
||||
### Credits
|
||||
|
||||
The project will be rewritten and the complete source will be a available soon.
|
||||
The android package id is currently <em>"re.jcg.playmusicexporter"</em>. This will be replaced by the old id <em>"de.arcus.playmusicexporter"</em> when the new code is done.
|
||||
This is a fork off [David Schulte's original project](https://github.com/Arcus92/PlayMusicExporter).
|
||||
Most of the work has been done by him, me and the other collaborators just have improved his work
|
||||
in some minor ways, like making the app more usable and configurable. The exporting itself, the hard
|
||||
part, has been done by him.
|
||||
|
||||
You can get the <em>old</em> working version [here](https://www.david-schulte.de/en/play-music-exporter-updater/)!
|
||||
This project uses the [mp3agic library](https://github.com/mpatric/mp3agic)
|
||||
by [Michael Patricios (mpatric)](https://github.com/mpatric).
|
||||
|
||||
### Contributing
|
||||
|
||||
#### What can you contribute?
|
||||
|
||||
Implement features you want to see, or take a look at the issue tracker and fix broken things. Before making bigger changes, see that a contributor is supporting the inclusion, so that you can be sure it will be merged. (See Discuss Ideas below)
|
||||
|
||||
#### How to contribute:
|
||||
|
||||
If you want to contribute to this project, fork off of develop,
|
||||
implement what you want to implement, and submit a pull request back into develop.
|
||||
After testing it, if enough of the collaborators like it, we will merge it.
|
||||
You might want to discuss your ideas with us first though:
|
||||
|
||||
#### Discuss Ideas
|
||||
|
||||
If you have any questions, the easiest way to get help is asking in the #playmusicexporter IRC channel on freenode. You can join that on webchat.freenode.net, or preferably, if you are not really an IRC guy anyway, try the corresponding matrix room [#playmusicexporter:jcg.re](https://matrix.to/#/#playmusicexporter:jcg.re). They are bridged together, so everything that happens in one of the rooms gets automatically transfered to the other one too.
|
||||
|
||||
### Supporting this project
|
||||
|
||||
If you want to support this project, the best way to do so is contributing source code to the project (see above), or help with illustrations, if that is something you're good at! (I am most certainly not good at creating icons and such things)
|
||||
|
||||
If you are unable to do that, but you still want to support this project, you could send me some bitcoins instead: 1NdzpDWPQ53xWT5fraGPZX5F9XrKiPBXjp
|
||||
|
||||
I will distribute bitcoins send to me to others helping on the project too, so that other people contributing also have a piece of the cake.
|
||||
|
||||
### Copyright
|
||||
|
||||
Copyright (c) 2016 Jan Christian Grünhage. See LICENSE.txt for details.
|
||||
Copyright (c) 2015 David Schulte. See LICENSE.txt for details.
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
|
@ -29,7 +27,7 @@ buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.2'
|
||||
classpath 'com.android.tools.build:gradle:2.4.0-alpha6'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,5 +36,4 @@ allprojects {
|
|||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ android {
|
|||
buildToolsVersion "25.0.2"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
minSdkVersion 11
|
||||
targetSdkVersion 25
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
@ -42,5 +42,5 @@ android {
|
|||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:25.1.0'
|
||||
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||
}
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":framework" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="PlayMusicExporter" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":framework" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<afterSyncTasks>
|
||||
<task>generateDebugSources</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" />
|
||||
<option name="LIBRARY_PROJECT" value="true" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
|
||||
<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/jni" 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/jni" 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/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/jni" 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/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/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||
<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/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotations" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-compat/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-ui/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-utils/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-fragment/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-media-compat/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/typedefs.txt" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="support-compat-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-fragment-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="animated-vector-drawable-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-core-ui-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-media-compat-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-vector-drawable-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-core-utils-25.1.0" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -24,7 +24,9 @@ package de.arcus.framework.crashhandler;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import de.arcus.framework.BuildConfig;
|
||||
import de.arcus.framework.activities.CrashActivity;
|
||||
import de.arcus.framework.logger.Logger;
|
||||
|
||||
|
@ -70,13 +72,29 @@ public class CrashHandler implements Thread.UncaughtExceptionHandler {
|
|||
|
||||
StringBuilder logBuilder = new StringBuilder();
|
||||
|
||||
//Device Information
|
||||
logBuilder.append("---------- Device Information -----------\n");
|
||||
logBuilder.append("Manufacturer: ").append(Build.MANUFACTURER).append("\n");
|
||||
logBuilder.append("Model: ").append(Build.MODEL).append("\n");
|
||||
logBuilder.append("Device API: ").append(Build.VERSION.RELEASE).append("\n");
|
||||
logBuilder.append("Product: ").append(Build.PRODUCT).append("\n");
|
||||
logBuilder.append("Build Number: ").append(Build.DISPLAY).append("\n");
|
||||
logBuilder.append("Build Tags: ").append(Build.TAGS).append("\n");
|
||||
logBuilder.append("\n");
|
||||
|
||||
|
||||
// Information
|
||||
logBuilder.append("---------- Information -----------\n");
|
||||
logBuilder.append("---------- App Information -----------\n");
|
||||
logBuilder.append("PackageName: ").append(mActivity.getPackageName()).append("\n");
|
||||
logBuilder.append("Crashed activity: ").append(mActivity.getLocalClassName()).append("\n");
|
||||
logBuilder.append("Version number: ").append(BuildConfig.VERSION_NAME).append("\n");
|
||||
logBuilder.append("Version code: ").append(BuildConfig.VERSION_CODE).append("\n");
|
||||
logBuilder.append("\n");
|
||||
|
||||
|
||||
logBuilder.append("----------- Exception ------------\n");
|
||||
logBuilder.append(ex.getMessage()).append("\n");
|
||||
logBuilder.append(ex.getClass().getCanonicalName()).append(" at \n");
|
||||
|
||||
// Log stack trace
|
||||
for (StackTraceElement stackTraceElement : ex.getStackTrace())
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 David Schulte
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package de.arcus.framework.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* Helper class to read and write app settings without to care about to open and close an editor
|
||||
*/
|
||||
public class AppSettings {
|
||||
/**
|
||||
* The default settings file
|
||||
*/
|
||||
public static final String DEFAULT_SETTINGS_FILENAME = "app_settings";
|
||||
|
||||
/**
|
||||
* The preferences
|
||||
*/
|
||||
private SharedPreferences mSharedPreferences;
|
||||
|
||||
/**
|
||||
* Creates a new instance of AppSettings that access to the default settings file
|
||||
* @param context Context of the app
|
||||
*/
|
||||
public AppSettings(Context context) {
|
||||
this(context, DEFAULT_SETTINGS_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of AppSettings that access to a specific settings file
|
||||
* @param context Context of the app
|
||||
*/
|
||||
public AppSettings(Context context, String settingsFilename) {
|
||||
mSharedPreferences = context.getSharedPreferences(settingsFilename, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string from the settings
|
||||
* @param key Key of the setting
|
||||
* @param defValue Default value which is returned if the key doesn't exists
|
||||
* @return Value
|
||||
*/
|
||||
public String getString(String key, String defValue) {
|
||||
return mSharedPreferences.getString(key, defValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a boolean from the settings
|
||||
* @param key Key of the setting
|
||||
* @param defValue Default value which is returned if the key doesn't exists
|
||||
* @return Value
|
||||
*/
|
||||
public boolean getBoolean(String key, boolean defValue) {
|
||||
return mSharedPreferences.getBoolean(key, defValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a float from the settings
|
||||
* @param key Key of the setting
|
||||
* @param defValue Default value which is returned if the key doesn't exists
|
||||
* @return Value
|
||||
*/
|
||||
public float getFloat(String key, float defValue) {
|
||||
return mSharedPreferences.getFloat(key, defValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an int from the settings
|
||||
* @param key Key of the setting
|
||||
* @param defValue Default value which is returned if the key doesn't exists
|
||||
* @return Value
|
||||
*/
|
||||
public int getInt(String key, int defValue) {
|
||||
return mSharedPreferences.getInt(key, defValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a long from the settings
|
||||
* @param key Key of the setting
|
||||
* @param defValue Default value which is returned if the key doesn't exists
|
||||
* @return Value
|
||||
*/
|
||||
public long getLong(String key, long defValue) {
|
||||
return mSharedPreferences.getLong(key, defValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a uri from the settings
|
||||
* @param key Key of the setting
|
||||
* @param defValue Default value which is returned if the key doesn't exists
|
||||
* @return Value
|
||||
*/
|
||||
public Uri getUri(String key, Uri defValue) {
|
||||
if (mSharedPreferences.contains(key))
|
||||
return Uri.parse(mSharedPreferences.getString(key, ""));
|
||||
else return defValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an enum value from the settings
|
||||
* @param key Key of the setting
|
||||
* @param defValue Default value which is returned if the key doesn't exists
|
||||
* @param <E> The enum type
|
||||
* @return Value
|
||||
*/
|
||||
public <E extends Enum<E>> E getEnum(String key, E defValue) {
|
||||
String value = mSharedPreferences.getString(key, defValue.name());
|
||||
|
||||
// Null check
|
||||
if (value != null) {
|
||||
// Checks all enum values
|
||||
for (E constant : defValue.getDeclaringClass().getEnumConstants()) {
|
||||
if (value.equals(constant.name()))
|
||||
return constant;
|
||||
}
|
||||
}
|
||||
// Return default
|
||||
return defValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the settings contains a specific key
|
||||
* @param key Key of the setting
|
||||
* @return Returns whether the settings contains a specific key
|
||||
*/
|
||||
public boolean contains(String key) {
|
||||
return mSharedPreferences.contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an setting from the settings
|
||||
* @param key Key of the setting
|
||||
*/
|
||||
public void remove(String key) {
|
||||
// Opens the editor
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
|
||||
// Removes the key
|
||||
editor.remove(key);
|
||||
|
||||
// Commits the change
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a string to the settings
|
||||
* @param key Key of the setting
|
||||
* @param value Value
|
||||
*/
|
||||
public void setString(String key, String value) {
|
||||
// Opens the editor
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
|
||||
editor.putString(key, value);
|
||||
|
||||
// Commits the change
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a boolean to the settings
|
||||
* @param key Key of the setting
|
||||
* @param value Value
|
||||
*/
|
||||
public void setBoolean(String key, boolean value) {
|
||||
// Opens the editor
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
|
||||
editor.putBoolean(key, value);
|
||||
|
||||
// Commits the change
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a float to the settings
|
||||
* @param key Key of the setting
|
||||
* @param value Value
|
||||
*/
|
||||
public void setFloat(String key, float value) {
|
||||
// Opens the editor
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
|
||||
editor.putFloat(key, value);
|
||||
|
||||
// Commits the change
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves an int to the settings
|
||||
* @param key Key of the setting
|
||||
* @param value Value
|
||||
*/
|
||||
public void setInt(String key, int value) {
|
||||
// Opens the editor
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
|
||||
editor.putInt(key, value);
|
||||
|
||||
// Commits the change
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a long to the settings
|
||||
* @param key Key of the setting
|
||||
* @param value Value
|
||||
*/
|
||||
public void setLong(String key, long value) {
|
||||
// Opens the editor
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
|
||||
editor.putLong(key, value);
|
||||
|
||||
// Commits the change
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a uri to the settings
|
||||
* @param key Key of the setting
|
||||
* @param value Value
|
||||
*/
|
||||
public void setUri(String key, Uri value) {
|
||||
// Opens the editor
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
|
||||
editor.putString(key, value.toString());
|
||||
|
||||
// Commits the change
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves an enum value to the settings
|
||||
* @param key Key of the setting
|
||||
* @param value Value
|
||||
*/
|
||||
public <E extends Enum<E>> void setEnum(String key, E value) {
|
||||
// Opens the editor
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
|
||||
editor.putString(key, value.name());
|
||||
|
||||
// Commits the change
|
||||
editor.commit();
|
||||
}
|
||||
}
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
package de.arcus.framework.superuser;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import de.arcus.framework.logger.Logger;
|
||||
|
@ -130,4 +132,20 @@ public class SuperUser {
|
|||
sessionStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void askForPermissionInBackground(final SuperUserPermissionRequestListener listener) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
boolean hasPermissions;
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
hasPermissions = askForPermissions();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
listener.superUserGranted(hasPermissions);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package de.arcus.framework.superuser;
|
||||
|
||||
public interface SuperUserPermissionRequestListener {
|
||||
void superUserGranted(boolean granted);
|
||||
}
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
package de.arcus.framework.utils;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -29,6 +31,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -41,10 +44,12 @@ public class FileTools {
|
|||
/**
|
||||
* Private constructor
|
||||
*/
|
||||
private FileTools() {}
|
||||
private FileTools() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory if it not exists
|
||||
*
|
||||
* @param dir Directory path
|
||||
* @return Returns true if the directory was created or already exists
|
||||
*/
|
||||
|
@ -74,6 +79,7 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Checks if the directory exists
|
||||
*
|
||||
* @param dir Path of the file
|
||||
* @return Return whether the directory exists
|
||||
*/
|
||||
|
@ -86,6 +92,7 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Creates an empty file
|
||||
*
|
||||
* @param file File path
|
||||
* @return Returns true if the file was successfully created
|
||||
*/
|
||||
|
@ -97,7 +104,7 @@ public class FileTools {
|
|||
return (new File(file)).createNewFile();
|
||||
} catch (IOException e) {
|
||||
// Failed
|
||||
Logger.getInstance().logError("FileCreate", "Could not create file: " + e.getMessage());
|
||||
Logger.getInstance().logError("FileCreate", "Could not create file: " + e.getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -105,7 +112,8 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Moves a file
|
||||
* @param src Soruce path
|
||||
*
|
||||
* @param src Soruce path
|
||||
* @param dest Destination path
|
||||
* @return Return whether the moving was successful
|
||||
*/
|
||||
|
@ -121,7 +129,8 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Copies a stream
|
||||
* @param inputStream Source stream
|
||||
*
|
||||
* @param inputStream Source stream
|
||||
* @param outputStream Destination stream
|
||||
* @return Return whether the stream was copied successful
|
||||
*/
|
||||
|
@ -154,7 +163,8 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Copies a file
|
||||
* @param src Source path
|
||||
*
|
||||
* @param src Source path
|
||||
* @param dest Destination path
|
||||
* @return Return whether the file was copied successful
|
||||
*/
|
||||
|
@ -194,6 +204,7 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Deletes a file
|
||||
*
|
||||
* @param file Path of the file
|
||||
* @return Returns whether the deleting was successful
|
||||
*/
|
||||
|
@ -204,6 +215,7 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Checks if the file exists
|
||||
*
|
||||
* @param file Path of the file
|
||||
* @return Return whether the file exists
|
||||
*/
|
||||
|
@ -216,6 +228,7 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Checks whether the file or directory is a link
|
||||
*
|
||||
* @param path Path of the file / directory
|
||||
* @return Returns whether the file or directory is a link
|
||||
*/
|
||||
|
@ -234,6 +247,7 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Gets the root canonical file of a symbolic link
|
||||
*
|
||||
* @param path The path
|
||||
* @return The root file
|
||||
*/
|
||||
|
@ -243,6 +257,7 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Gets the root canonical file of a symbolic link
|
||||
*
|
||||
* @param file The file
|
||||
* @return The root file
|
||||
*/
|
||||
|
@ -268,19 +283,20 @@ public class FileTools {
|
|||
|
||||
/**
|
||||
* Gets all storages; eg. all sdcards
|
||||
*
|
||||
* @return List of all storages
|
||||
*/
|
||||
public static String[] getStorages() {
|
||||
List<String> storages = new ArrayList<>();
|
||||
|
||||
// Hard coded mount points
|
||||
final String[] mountPointBlacklist = new String[] { "/mnt/tmp", "/mnt/factory", "/mnt/obb", "/mnt/asec", "/mnt/secure", "/mnt/media_rw", "/mnt/shell", "/storage/emulated" };
|
||||
final String[] mountPointDirectories = new String[] { "/mnt", "/storage" };
|
||||
final String[] mountPoints = new String[] { "/sdcard", "/external_sd" };
|
||||
final String[] mountPointBlacklist = new String[]{"/mnt/tmp", "/mnt/factory", "/mnt/obb", "/mnt/asec", "/mnt/secure", "/mnt/media_rw", "/mnt/shell", "/storage/emulated"};
|
||||
final String[] mountPointDirectories = new String[]{"/mnt", "/storage"};
|
||||
final String[] mountPoints = new String[]{Environment.getExternalStorageDirectory().getAbsolutePath(), "/external_sd"};
|
||||
|
||||
|
||||
// Adds all mount point directories
|
||||
for(String mountPointDirectory : mountPointDirectories) {
|
||||
for (String mountPointDirectory : mountPointDirectories) {
|
||||
// Checks all subdirectories
|
||||
File dir = getRootCanonicalFile(mountPointDirectory);
|
||||
if (dir.exists() && dir.isDirectory()) {
|
||||
|
@ -300,7 +316,7 @@ public class FileTools {
|
|||
}
|
||||
|
||||
// Adds all direct mount points
|
||||
for(String mountPoint : mountPoints) {
|
||||
for (String mountPoint : mountPoints) {
|
||||
File file = getRootCanonicalFile(mountPoint);
|
||||
if (file.isDirectory() && file.canRead()) {
|
||||
if (!storages.contains(file.getAbsolutePath()))
|
||||
|
@ -309,9 +325,7 @@ public class FileTools {
|
|||
}
|
||||
|
||||
// Remove all blacklisted paths
|
||||
for (String blacklistPath : mountPointBlacklist) {
|
||||
storages.remove(blacklistPath);
|
||||
}
|
||||
storages.removeAll(Arrays.asList(mountPointBlacklist));
|
||||
|
||||
// Sort the list
|
||||
Collections.sort(storages);
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
|
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
#Fri Nov 25 23:05:46 CET 2016
|
||||
#Tue Mar 28 12:22:37 CEST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
|
@ -32,9 +30,12 @@ android {
|
|||
applicationId "re.jcg.playmusicexporter"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 102
|
||||
versionName '2.4.2'
|
||||
// TODO Change Version with releases
|
||||
versionCode 110
|
||||
versionName '0.9.6.0'
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
buildConfigField "java.util.Date", "BUILD_TIME", "new java.util.Date(" + System.currentTimeMillis() + "L)"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
|
@ -42,14 +43,23 @@ android {
|
|||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
targetCompatibility 1.8
|
||||
sourceCompatibility 1.8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile project(':framework')
|
||||
compile project(':playmusiclib')
|
||||
compile 'com.android.support:appcompat-v7:25.1.0'
|
||||
compile 'com.android.support:support-v4:25.1.0'
|
||||
compile 'com.android.support:design:25.1.0'
|
||||
compile 'com.android.support:support-vector-drawable:25.1.0'
|
||||
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||
compile 'com.android.support:support-v4:25.3.1'
|
||||
compile 'com.android.support:design:25.3.1'
|
||||
compile 'com.android.support:support-vector-drawable:25.3.1'
|
||||
compile 'com.github.paolorotolo:appintro:4.1.0'
|
||||
compile 'ly.count.android:sdk:16.12.2'
|
||||
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
|
||||
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
|
||||
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
|
||||
}
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":playmusicexporter" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="PlayMusicExporter" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":playmusicexporter" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<afterSyncTasks>
|
||||
<task>generateDebugSources</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_7" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
|
||||
<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/jni" 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/jni" 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/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/jni" 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/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/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||
<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/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-compat/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-ui/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-utils/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-fragment/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-media-compat/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/transition/25.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
|
||||
<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 25 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="design-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="transition-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-compat-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-fragment-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="animated-vector-drawable-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-core-ui-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-media-compat-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-vector-drawable-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="recyclerview-v7-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-core-utils-25.1.0" level="project" />
|
||||
<orderEntry type="module" module-name="playmusiclib" exported="" />
|
||||
<orderEntry type="module" module-name="framework" exported="" />
|
||||
<orderEntry type="library" exported="" name="mp3agic-0.8.4" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -24,14 +24,17 @@
|
|||
|
||||
<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.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:name=".PlayMusicExporter">
|
||||
<activity
|
||||
android:name=".activities.MusicContainerListActivity"
|
||||
android:label="@string/app_name">
|
||||
|
@ -48,11 +51,15 @@
|
|||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activities.MusicContainerListActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activities.Intro"
|
||||
android:label="Play Music Exporter Intro" />
|
||||
|
||||
<service android:name=".services.ExportService" />
|
||||
<service android:name=".services.ExportAllService" />
|
||||
<service android:name=".services.ExportAllJob"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"/>
|
||||
<service
|
||||
android:name=".services.ExportAllJob"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<meta-data
|
||||
android:name="crashhandler.email"
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package re.jcg.playmusicexporter;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
|
||||
|
||||
/**
|
||||
* Android Application.
|
||||
* Normally, we would not need to extend this, but it is required for
|
||||
* the leak detection library we use.
|
||||
* See {@link LeakCanary}
|
||||
*/
|
||||
public class PlayMusicExporter extends Application {
|
||||
@Override public void onCreate() {
|
||||
super.onCreate();
|
||||
if (LeakCanary.isInAnalyzerProcess(this)) {
|
||||
// This process is dedicated to LeakCanary for heap analysis.
|
||||
// You should not init your app in this process.
|
||||
return;
|
||||
}
|
||||
LeakCanary.install(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package re.jcg.playmusicexporter.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
|
||||
import com.github.paolorotolo.appintro.AppIntro;
|
||||
import com.github.paolorotolo.appintro.AppIntroFragment;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import de.arcus.framework.superuser.SuperUser;
|
||||
import re.jcg.playmusicexporter.R;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
|
||||
|
||||
public class Intro extends AppIntro {
|
||||
private static final String TAG = "PME_Intro";
|
||||
private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
|
||||
Fragment welcome;
|
||||
Fragment warning;
|
||||
Fragment storage;
|
||||
Fragment superuser;
|
||||
Fragment error;
|
||||
Fragment finish;
|
||||
|
||||
private void initFragments() {
|
||||
int color = ContextCompat.getColor(this, R.color.application_main);
|
||||
welcome = AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_welcome_title),
|
||||
getString(R.string.intro_welcome_description),
|
||||
R.drawable.ic_launcher_transparent,
|
||||
color);
|
||||
warning = AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_warning_title),
|
||||
getString(R.string.intro_warning_description),
|
||||
R.drawable.ic_warning_white,
|
||||
color);
|
||||
storage = AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_storage_title),
|
||||
getString(R.string.intro_storage_description),
|
||||
R.drawable.ic_folder_white,
|
||||
color);
|
||||
superuser = AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_superuser_title),
|
||||
getString(R.string.intro_superuser_description),
|
||||
R.drawable.ic_superuser,
|
||||
color);
|
||||
error = AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_error_title),
|
||||
getString(R.string.intro_error_description),
|
||||
R.drawable.ic_error_white,
|
||||
color);
|
||||
finish = AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_finish_title),
|
||||
getString(R.string.intro_finish_description),
|
||||
R.drawable.ic_launcher_transparent,
|
||||
color);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (getSupportActionBar() != null) getSupportActionBar().hide();
|
||||
showSkipButton(false);
|
||||
|
||||
initFragments();
|
||||
|
||||
addSlide(welcome);
|
||||
addSlide(warning);
|
||||
addSlide(storage);
|
||||
addSlide(superuser);
|
||||
addSlide(error);
|
||||
addSlide(finish);
|
||||
|
||||
pager.setPagingEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlideChanged(@Nullable Fragment oldFragment, @Nullable Fragment newFragment) {
|
||||
super.onSlideChanged(oldFragment, newFragment);
|
||||
logSlideChanged(oldFragment, newFragment);
|
||||
if (warning.equals(oldFragment) && storage.equals(newFragment)) {
|
||||
promptAcceptWarning();
|
||||
} else if (storage.equals(oldFragment) && superuser.equals(newFragment)) {
|
||||
requestStoragePermission();
|
||||
} else if (superuser.equals(oldFragment) && error.equals(newFragment)) {
|
||||
SuperUser.askForPermissionInBackground(granted -> {
|
||||
if (!granted) {
|
||||
AlertDialog.Builder builder =
|
||||
new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.dialog_superuser_access_denied_title);
|
||||
builder.setMessage(R.string.dialog_superuser_access_denied);
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_okay, (dialog, which)
|
||||
-> pager.setCurrentItem(pager.getCurrentItem() - 1));
|
||||
builder.show();
|
||||
}
|
||||
});
|
||||
} else if (error.equals(oldFragment) && finish.equals(newFragment)) {
|
||||
promptEnableErrorReporting();
|
||||
}
|
||||
}
|
||||
|
||||
private void promptEnableErrorReporting() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
Dialog.OnClickListener enable = (dialog, which) -> {
|
||||
PlayMusicExporterPreferences.setReportStats(true);
|
||||
dialog.dismiss();
|
||||
};
|
||||
Dialog.OnClickListener disable = (dialog, which) -> {
|
||||
PlayMusicExporterPreferences.setReportStats(false);
|
||||
dialog.dismiss();
|
||||
};
|
||||
builder.setTitle(R.string.error_alert_dialog_title);
|
||||
builder.setMessage(R.string.error_alert_dialog_message);
|
||||
builder.setCancelable(false);
|
||||
builder.setNegativeButton(R.string.no, disable);
|
||||
builder.setNeutralButton(R.string.whatever, enable);
|
||||
builder.setPositiveButton(R.string.yes, enable);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void requestStoragePermission() {
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
if (!(grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
||||
// Shows a warning and close the app
|
||||
AlertDialog.Builder builder =
|
||||
new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.dialog_storage_access_denied_title);
|
||||
builder.setMessage(R.string.dialog_storage_access_denied);
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_okay, (dialog, which)
|
||||
-> pager.setCurrentItem(pager.getCurrentItem() - 1));
|
||||
builder.show();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void promptAcceptWarning() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.warning_alert_dialog_title);
|
||||
builder.setMessage(R.string.warning_alert_dialog_message);
|
||||
builder.setCancelable(false);
|
||||
builder.setNegativeButton(getString(R.string.no), ((dialog, which)
|
||||
-> pager.setCurrentItem(pager.getCurrentItem() - 1)));
|
||||
builder.setPositiveButton(getString(R.string.yes), (((dialog, which) -> dialog.dismiss())));
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void logSlideChanged(@Nullable Fragment oldFragment, @Nullable Fragment newFragment) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
// Optional.ofNullable requires API level 24, and I won't do manual null checks.
|
||||
Log.i(TAG, "Fragment switched from {" +
|
||||
Optional.ofNullable(oldFragment).map(Fragment::toString).orElse("") +
|
||||
"} to {" +
|
||||
Optional.ofNullable(newFragment).map(Fragment::toString).orElse("") +
|
||||
"}.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDonePressed(Fragment currentFragment) {
|
||||
PlayMusicExporterPreferences.init(this);
|
||||
PlayMusicExporterPreferences.setSetupDone(true);
|
||||
startActivity(new Intent(this, MusicContainerListActivity.class));
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@
|
|||
package re.jcg.playmusicexporter.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
|
@ -41,9 +40,13 @@ import android.view.Menu;
|
|||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import de.arcus.framework.logger.Logger;
|
||||
import de.arcus.framework.crashhandler.CrashHandler;
|
||||
import de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException;
|
||||
import de.arcus.playmusiclib.exceptions.NoSuperUserException;
|
||||
import ly.count.android.sdk.Countly;
|
||||
import ly.count.android.sdk.DeviceId;
|
||||
import re.jcg.playmusicexporter.R;
|
||||
import re.jcg.playmusicexporter.fragments.MusicTrackListFragment;
|
||||
import re.jcg.playmusicexporter.fragments.MusicContainerListFragment;
|
||||
|
@ -55,6 +58,7 @@ import de.arcus.playmusiclib.datasources.ArtistDataSource;
|
|||
import de.arcus.playmusiclib.datasources.PlaylistDataSource;
|
||||
import de.arcus.playmusiclib.enums.ID3v2Version;
|
||||
import de.arcus.playmusiclib.items.MusicTrackList;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
|
||||
|
||||
/**
|
||||
* An activity representing a list of Tracks. This activity
|
||||
|
@ -104,59 +108,67 @@ public class MusicContainerListActivity extends AppCompatActivity
|
|||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_track_list);
|
||||
PlayMusicExporterPreferences.init(this);
|
||||
if (!PlayMusicExporterPreferences.getSetupDone()) {
|
||||
startActivity(new Intent(this, Intro.class));
|
||||
finish();
|
||||
} else {
|
||||
if (PlayMusicExporterPreferences.getReportStats()) {
|
||||
Countly.sharedInstance().init(this, getString(R.string.countly_url), getString(R.string.countly_token), null, DeviceId.Type.OPEN_UDID);
|
||||
Countly.sharedInstance().enableCrashReporting();
|
||||
}
|
||||
setContentView(R.layout.activity_track_list);
|
||||
|
||||
//Adds the crash handler to this class
|
||||
CrashHandler.addCrashHandler(this);
|
||||
|
||||
Logger.getInstance().logVerbose("Activity", "onCreate(" + this.getLocalClassName() + ")");
|
||||
Logger.getInstance().logVerbose("Activity", "onCreate(" + this.getLocalClassName() + ")");
|
||||
|
||||
// Setup ActionBar
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setTitle(R.string.app_name);
|
||||
// Setup ActionBar
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setTitle(R.string.app_name);
|
||||
}
|
||||
|
||||
|
||||
mNavigationDrawerFragment = (NavigationDrawerFragment)
|
||||
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
|
||||
|
||||
mNavigationDrawerFragment.setOnListViewChanged(this);
|
||||
|
||||
// Set up the drawer.
|
||||
mNavigationDrawerFragment.setUp(
|
||||
R.id.navigation_drawer,
|
||||
(DrawerLayout) findViewById(R.id.drawer_layout));
|
||||
|
||||
if (findViewById(R.id.track_detail_container) != null) {
|
||||
// The detail container view will be present only in the
|
||||
// large-screen layouts (res/values-large and
|
||||
// res/values-sw600dp). If this view is present, then the
|
||||
// activity should be in two-pane mode.
|
||||
mTwoPane = true;
|
||||
|
||||
// In two-pane mode, list items should be given the
|
||||
// 'activated' state when touched.
|
||||
((MusicContainerListFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.fragment_main))
|
||||
.setActivateOnItemClick(true);
|
||||
}
|
||||
|
||||
boolean waitForPermissions = false;
|
||||
|
||||
// Check file system permissions
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
|
||||
waitForPermissions = true;
|
||||
}
|
||||
|
||||
if (!waitForPermissions)
|
||||
loadPlayMusicExporter();
|
||||
}
|
||||
|
||||
|
||||
mNavigationDrawerFragment = (NavigationDrawerFragment)
|
||||
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
|
||||
|
||||
mNavigationDrawerFragment.setOnListViewChanged(this);
|
||||
|
||||
// Set up the drawer.
|
||||
mNavigationDrawerFragment.setUp(
|
||||
R.id.navigation_drawer,
|
||||
(DrawerLayout) findViewById(R.id.drawer_layout));
|
||||
|
||||
if (findViewById(R.id.track_detail_container) != null) {
|
||||
// The detail container view will be present only in the
|
||||
// large-screen layouts (res/values-large and
|
||||
// res/values-sw600dp). If this view is present, then the
|
||||
// activity should be in two-pane mode.
|
||||
mTwoPane = true;
|
||||
|
||||
// In two-pane mode, list items should be given the
|
||||
// 'activated' state when touched.
|
||||
((MusicContainerListFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.fragment_main))
|
||||
.setActivateOnItemClick(true);
|
||||
}
|
||||
|
||||
boolean waitForPermissions = false;
|
||||
|
||||
// Check file system permissions
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
|
||||
waitForPermissions = true;
|
||||
}
|
||||
|
||||
if (!waitForPermissions)
|
||||
loadPlayMusicExporter();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -176,24 +188,18 @@ public class MusicContainerListActivity extends AppCompatActivity
|
|||
builder.setTitle(R.string.dialog_storage_access_denied_title);
|
||||
builder.setMessage(R.string.dialog_storage_access_denied);
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_okay, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// Close the app
|
||||
MusicContainerListActivity.this.finish();
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(R.string.text_okay, (dialog, which) -> MusicContainerListActivity.this.finish());
|
||||
builder.show();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the PlayMusicExporter lib and shows the list
|
||||
*/
|
||||
private void loadPlayMusicExporter()
|
||||
{
|
||||
private void loadPlayMusicExporter() {
|
||||
// Gets the running instance
|
||||
mPlayMusicManager = PlayMusicManager.getInstance();
|
||||
|
||||
|
@ -211,8 +217,8 @@ public class MusicContainerListActivity extends AppCompatActivity
|
|||
mPlayMusicManager.setID3EnableArtwork(true);
|
||||
mPlayMusicManager.setID3EnableFallback(true);
|
||||
mPlayMusicManager.setID3v2Version(ID3v2Version.ID3v23);
|
||||
mPlayMusicManager.setID3ArtworkFormat(Bitmap.CompressFormat.JPEG);
|
||||
mPlayMusicManager.setID3ArtworkMaximumSize(512);
|
||||
mPlayMusicManager.setID3ArtworkFormat(Bitmap.CompressFormat.PNG);
|
||||
mPlayMusicManager.setID3ArtworkMaximumSize(PlayMusicExporterPreferences.getAlbumArtSize());
|
||||
|
||||
} catch (Exception e) {
|
||||
Logger.getInstance().logError("SetupPlayMusicExporter", e.toString());
|
||||
|
@ -255,7 +261,7 @@ public class MusicContainerListActivity extends AppCompatActivity
|
|||
MusicContainerListFragment musicTrackListFragment = (MusicContainerListFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.fragment_main);
|
||||
|
||||
switch(mViewType) {
|
||||
switch (mViewType) {
|
||||
case Album:
|
||||
// Load all albums to the list
|
||||
AlbumDataSource dataSourceAlbum = new AlbumDataSource(mPlayMusicManager);
|
||||
|
@ -322,9 +328,24 @@ public class MusicContainerListActivity extends AppCompatActivity
|
|||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.music_track_list, menu);
|
||||
|
||||
MenuItem itemRefreshLibrary = menu.findItem(R.id.action_refresh);
|
||||
itemRefreshLibrary.setOnMenuItemClickListener(item ->
|
||||
{
|
||||
try {
|
||||
mPlayMusicManager.reloadDatabase();
|
||||
mPlayMusicManager = null;
|
||||
loadPlayMusicExporter();
|
||||
Toast.makeText(this, R.string.database_reloaded, Toast.LENGTH_SHORT).show();
|
||||
} catch (NoSuperUserException | CouldNotOpenDatabaseException e) {
|
||||
Toast.makeText(this, R.string.dialog_superuser_access_denied_title, Toast.LENGTH_SHORT).show();
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Finds the search item and create the search view
|
||||
MenuItem itemSearch = menu.findItem(R.id.action_search);
|
||||
mSearchView = (SearchView)MenuItemCompat.getActionView(itemSearch);
|
||||
mSearchView = (SearchView) MenuItemCompat.getActionView(itemSearch);
|
||||
|
||||
if (mSearchView != null) {
|
||||
// Sets the search listener
|
||||
|
@ -352,4 +373,18 @@ public class MusicContainerListActivity extends AppCompatActivity
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
if (PlayMusicExporterPreferences.getReportStats())
|
||||
Countly.sharedInstance().onStart(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
if (PlayMusicExporterPreferences.getReportStats())
|
||||
Countly.sharedInstance().onStop();
|
||||
super.onStop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ import android.annotation.TargetApi;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -15,20 +13,19 @@ import android.preference.Preference;
|
|||
import android.preference.PreferenceActivity;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.RingtonePreference;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.support.v4.app.NavUtils;
|
||||
|
||||
import re.jcg.playmusicexporter.BuildConfig;
|
||||
import re.jcg.playmusicexporter.R;
|
||||
import re.jcg.playmusicexporter.services.ExportAllJob;
|
||||
import re.jcg.playmusicexporter.services.ExportAllService;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
|
||||
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
||||
|
||||
/**
|
||||
* A {@link PreferenceActivity} that presents a set of application settings. On
|
||||
* handset devices, settings are presented as a single list. On tablets,
|
||||
|
@ -42,36 +39,35 @@ import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
|||
*/
|
||||
public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
private static final String TAG = "MusicExporter_Settings";
|
||||
private static final int OPEN_DOCUMENT_TREE_REQUEST_CODE = 0;
|
||||
private static final int REQUEST_CODE_OPEN_DOCUMENT_TREE_ALBA_PATH = 0;
|
||||
private static final int REQUEST_CODE_OPEN_DOCUMENT_TREE_GROUPS_PATH = 1;
|
||||
private static final int REQUEST_CODE_OPEN_DOCUMENT_TREE_AUTO_PATH = 2;
|
||||
/**
|
||||
* A preference value change listener that updates the preference's summary
|
||||
* to reflect its new value.
|
||||
*/
|
||||
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object value) {
|
||||
String stringValue = value.toString();
|
||||
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = (preference, value) -> {
|
||||
String stringValue = value.toString();
|
||||
|
||||
if (preference instanceof ListPreference) {
|
||||
// For list preferences, look up the correct display value in
|
||||
// the preference's 'entries' list.
|
||||
ListPreference listPreference = (ListPreference) preference;
|
||||
int index = listPreference.findIndexOfValue(stringValue);
|
||||
if (preference instanceof ListPreference) {
|
||||
// For list preferences, look up the correct display value in
|
||||
// the preference's 'entries' list.
|
||||
ListPreference listPreference = (ListPreference) preference;
|
||||
int index = listPreference.findIndexOfValue(stringValue);
|
||||
|
||||
// Set the summary to reflect the new value.
|
||||
preference.setSummary(
|
||||
index >= 0
|
||||
? listPreference.getEntries()[index]
|
||||
: null);
|
||||
// Set the summary to reflect the new value.
|
||||
preference.setSummary(
|
||||
index >= 0
|
||||
? listPreference.getEntries()[index]
|
||||
: null);
|
||||
|
||||
} else {
|
||||
// For all other preferences, set the summary to the value's
|
||||
// simple string representation.
|
||||
preference.setSummary(stringValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// For all other preferences, set the summary to the value's
|
||||
// simple string representation.
|
||||
preference.setSummary(stringValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -83,26 +79,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a preference's summary to its value. More specifically, when the
|
||||
* preference's value is changed, its summary (line of text below the
|
||||
* preference title) is updated to reflect the value. The summary is also
|
||||
* immediately updated upon calling this method. The exact display format is
|
||||
* dependent on the type of preference.
|
||||
*
|
||||
* @see #sBindPreferenceSummaryToValueListener
|
||||
*/
|
||||
private static void bindPreferenceSummaryToValue(Preference preference) {
|
||||
// Set the listener to watch for value changes.
|
||||
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
||||
|
||||
// Trigger the listener immediately with the preference's
|
||||
// current value.
|
||||
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(preference.getContext())
|
||||
.getString(preference.getKey(), ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -148,6 +124,9 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void onBuildHeaders(List<Header> target) {
|
||||
loadHeadersFromResource(R.xml.pref_headers, target);
|
||||
if (BuildConfig.DEBUG) {
|
||||
loadHeadersFromResource(R.xml.pref_debug_header, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,6 +142,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
}
|
||||
|
||||
public static class ExportPreferenceFragment extends PreferenceFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -170,21 +150,33 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
setHasOptionsMenu(true);
|
||||
|
||||
|
||||
findPreference("preference_export_path").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), OPEN_DOCUMENT_TREE_REQUEST_CODE);
|
||||
return true;
|
||||
}
|
||||
findPreference("preference_alba_export_path").setOnPreferenceClickListener(preference -> {
|
||||
startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), REQUEST_CODE_OPEN_DOCUMENT_TREE_ALBA_PATH);
|
||||
return true;
|
||||
});
|
||||
findPreference("preference_groups_export_path").setOnPreferenceClickListener(preference -> {
|
||||
startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), REQUEST_CODE_OPEN_DOCUMENT_TREE_GROUPS_PATH);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
switch (requestCode) {
|
||||
case OPEN_DOCUMENT_TREE_REQUEST_CODE:
|
||||
case REQUEST_CODE_OPEN_DOCUMENT_TREE_ALBA_PATH:
|
||||
if (resultCode == RESULT_OK) {
|
||||
Uri treeUri = resultData.getData();
|
||||
PreferenceManager.getDefaultSharedPreferences(getActivity()).edit().putString("preference_export_tree_uri", treeUri.toString()).apply();
|
||||
PlayMusicExporterPreferences.init(getActivity());
|
||||
PlayMusicExporterPreferences.setAlbaExportPath(treeUri);
|
||||
getActivity().getContentResolver().takePersistableUriPermission(treeUri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
Log.i(TAG, "Selected " + treeUri.toString());
|
||||
}
|
||||
break;
|
||||
case REQUEST_CODE_OPEN_DOCUMENT_TREE_GROUPS_PATH:
|
||||
if (resultCode == RESULT_OK) {
|
||||
Uri treeUri = resultData.getData();
|
||||
PlayMusicExporterPreferences.init(getActivity());
|
||||
PlayMusicExporterPreferences.setGroupsExportPath(treeUri);
|
||||
getActivity().getContentResolver().takePersistableUriPermission(treeUri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
Log.i(TAG, "Selected " + treeUri.toString());
|
||||
|
@ -208,11 +200,71 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
}
|
||||
|
||||
public static class AutomationPreferenceFragment extends PreferenceFragment {
|
||||
|
||||
|
||||
/**
|
||||
* Little helper method to get the context.
|
||||
* If the Android version is high enough, the newer {@link PreferenceFragment#getContext()} is used,
|
||||
* else, the older {@link PreferenceFragment#getActivity()}, since activities are contexts too.
|
||||
*
|
||||
* @return The context.
|
||||
*/
|
||||
@Override
|
||||
public Context getContext() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return super.getContext();
|
||||
} else {
|
||||
return super.getActivity();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_automation);
|
||||
setHasOptionsMenu(true);
|
||||
//Schedules an export with the current settings.
|
||||
ExportAllJob.scheduleExport(getContext());
|
||||
|
||||
setupOnClickListeners();
|
||||
}
|
||||
|
||||
private void setupOnClickListeners() {
|
||||
findPreference(PlayMusicExporterPreferences.AUTO_EXPORT_PATH).setOnPreferenceClickListener(preference -> {
|
||||
startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), REQUEST_CODE_OPEN_DOCUMENT_TREE_AUTO_PATH);
|
||||
return true;
|
||||
});
|
||||
|
||||
//For every setting that is used to define the export job, setup a listener that
|
||||
setupRescheduler(PlayMusicExporterPreferences.AUTO_EXPORT_REQUIRE_CHARGING);
|
||||
setupRescheduler(PlayMusicExporterPreferences.AUTO_EXPORT_REQUIRE_UNMETERED);
|
||||
setupRescheduler(PlayMusicExporterPreferences.AUTO_EXPORT_ENABLED);
|
||||
setupRescheduler(PlayMusicExporterPreferences.AUTO_EXPORT_FREQUENCY);
|
||||
}
|
||||
|
||||
private void setupRescheduler(String preference) {
|
||||
findPreference(preference).setOnPreferenceClickListener(preference1 -> {
|
||||
ExportAllJob.scheduleExport(getContext());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_OPEN_DOCUMENT_TREE_AUTO_PATH:
|
||||
if (resultCode == RESULT_OK) {
|
||||
Uri treeUri = resultData.getData();
|
||||
PlayMusicExporterPreferences.init(getActivity());
|
||||
PlayMusicExporterPreferences.setAutoExportPath(treeUri);
|
||||
getActivity().getContentResolver().takePersistableUriPermission(treeUri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
Log.i(TAG, "Selected " + treeUri.toString());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.i(TAG, "Received activityResult with unknown requestCode");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -232,6 +284,10 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_about);
|
||||
setHasOptionsMenu(true);
|
||||
findPreference("about_version_number").setSummary(BuildConfig.VERSION_NAME);
|
||||
findPreference("about_build_date")
|
||||
.setSummary(SimpleDateFormat.getDateInstance().format(BuildConfig.BUILD_TIME));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -252,13 +308,12 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
addPreferencesFromResource(R.xml.pref_debug);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
findPreference("debug_trigger_export_all").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Log.i(TAG, "Debug Trigger Export All Click registered.");
|
||||
ExportAllService.startExport(getActivity());
|
||||
return true;
|
||||
}
|
||||
findPreference("debug_trigger_export_all").setOnPreferenceClickListener(preference -> {
|
||||
ExportAllService.startExport(getActivity());
|
||||
return true;
|
||||
});
|
||||
findPreference("debug_test_crash_handler").setOnPreferenceClickListener(preference -> {
|
||||
throw new IllegalStateException("Test for the crash handler.");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -79,10 +79,7 @@ public class MusicContainerListFragment extends ListFragment {
|
|||
* A dummy implementation of the {@link Callbacks} interface that does
|
||||
* nothing. Used only when this fragment is not attached to an activity.
|
||||
*/
|
||||
private static Callbacks sDummyCallbacks = new Callbacks() {
|
||||
@Override
|
||||
public void onItemSelected(MusicTrackList musicTrackList) {
|
||||
}
|
||||
private static Callbacks sDummyCallbacks = musicTrackList -> {
|
||||
};
|
||||
|
||||
private MusicContainerListAdapter mMusicTrackListAdapter;
|
||||
|
@ -112,9 +109,7 @@ public class MusicContainerListFragment extends ListFragment {
|
|||
// Null check
|
||||
if (list != null) {
|
||||
// Copy the list
|
||||
for (MusicTrackList musicTrackList : list) {
|
||||
newList.add(musicTrackList);
|
||||
}
|
||||
newList.addAll(list);
|
||||
}
|
||||
|
||||
// Set the list in the adapter
|
||||
|
@ -145,7 +140,8 @@ public class MusicContainerListFragment extends ListFragment {
|
|||
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
|
||||
}
|
||||
}
|
||||
// DEPRECATED
|
||||
|
||||
// DEPRECATED
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
|
|
|
@ -22,17 +22,16 @@
|
|||
|
||||
package re.jcg.playmusicexporter.fragments;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.os.PowerManager;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
@ -44,7 +43,7 @@ import re.jcg.playmusicexporter.activities.MusicTrackListActivity;
|
|||
import re.jcg.playmusicexporter.adapter.MusicTrackListAdapter;
|
||||
import re.jcg.playmusicexporter.items.SelectedTrack;
|
||||
import re.jcg.playmusicexporter.items.SelectedTrackList;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterSettings;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
|
||||
import re.jcg.playmusicexporter.utils.ArtworkViewLoader;
|
||||
import re.jcg.playmusicexporter.utils.MusicPathBuilder;
|
||||
import de.arcus.playmusiclib.PlayMusicManager;
|
||||
|
@ -65,6 +64,7 @@ public class MusicTrackListFragment extends Fragment {
|
|||
*/
|
||||
public static final String ARG_MUSIC_TRACK_LIST_ID = "music_track_list_id";
|
||||
public static final String ARG_MUSIC_TRACK_LIST_TYPE = "music_track_list_type";
|
||||
private PowerManager.WakeLock m_CPULock;
|
||||
|
||||
/**
|
||||
* The track list
|
||||
|
@ -113,7 +113,7 @@ public class MusicTrackListFragment extends Fragment {
|
|||
*/
|
||||
public void selectAll() {
|
||||
// Select all tracks
|
||||
for(int i = 0; i < mMusicTrackAdapter.getCount(); i++ ) {
|
||||
for (int i = 0; i < mMusicTrackAdapter.getCount(); i++) {
|
||||
MusicTrack musicTrack = mMusicTrackAdapter.getItem(i);
|
||||
|
||||
selectTrack(musicTrack, null, TrackSelectionState.Select);
|
||||
|
@ -127,7 +127,7 @@ public class MusicTrackListFragment extends Fragment {
|
|||
*/
|
||||
public void deselectAll() {
|
||||
// Deselect all tracks
|
||||
for(int i = 0; i < mMusicTrackAdapter.getCount(); i++ ) {
|
||||
for (int i = 0; i < mMusicTrackAdapter.getCount(); i++) {
|
||||
MusicTrack musicTrack = mMusicTrackAdapter.getItem(i);
|
||||
|
||||
selectTrack(musicTrack, null, TrackSelectionState.Deselect);
|
||||
|
@ -140,8 +140,10 @@ public class MusicTrackListFragment extends Fragment {
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
PowerManager powerManager = (PowerManager)this.getContext().getSystemService(Context.POWER_SERVICE);
|
||||
m_CPULock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExportAllService");
|
||||
if (getArguments().containsKey(ARG_MUSIC_TRACK_LIST_ID)
|
||||
&& getArguments().containsKey(ARG_MUSIC_TRACK_LIST_TYPE)) {
|
||||
&& getArguments().containsKey(ARG_MUSIC_TRACK_LIST_TYPE)) {
|
||||
|
||||
// Loads the track list
|
||||
long id = getArguments().getLong(ARG_MUSIC_TRACK_LIST_ID);
|
||||
|
@ -162,7 +164,7 @@ public class MusicTrackListFragment extends Fragment {
|
|||
|
||||
// Show the dummy content as text in a TextView.
|
||||
if (mMusicTrackList != null) {
|
||||
mListView = (ListView)rootView.findViewById(R.id.list_music_track);
|
||||
mListView = (ListView) rootView.findViewById(R.id.list_music_track);
|
||||
mMusicTrackAdapter = new MusicTrackListAdapter(getActivity());
|
||||
|
||||
mMusicTrackAdapter.setShowArtworks(mMusicTrackList.getShowArtworkInTrack());
|
||||
|
@ -174,17 +176,17 @@ public class MusicTrackListFragment extends Fragment {
|
|||
ImageView imageView;
|
||||
|
||||
// Sets the artwork image
|
||||
imageView = (ImageView)headerView.findViewById(R.id.image_music_track_artwork);
|
||||
imageView = (ImageView) headerView.findViewById(R.id.image_music_track_artwork);
|
||||
|
||||
// Loads the artwork
|
||||
ArtworkViewLoader.loadImage(mMusicTrackList, imageView, R.drawable.cd_case);
|
||||
|
||||
// Sets the title
|
||||
textView = (TextView)headerView.findViewById(R.id.text_music_track_list_title);
|
||||
textView = (TextView) headerView.findViewById(R.id.text_music_track_list_title);
|
||||
textView.setText(mMusicTrackList.getTitle());
|
||||
|
||||
// Sets the description
|
||||
textView = (TextView)headerView.findViewById(R.id.text_music_track_list_description);
|
||||
textView = (TextView) headerView.findViewById(R.id.text_music_track_list_description);
|
||||
textView.setText(mMusicTrackList.getDescription());
|
||||
|
||||
mListView.addHeaderView(headerView);
|
||||
|
@ -194,36 +196,36 @@ public class MusicTrackListFragment extends Fragment {
|
|||
mListView.setAdapter(mMusicTrackAdapter);
|
||||
|
||||
// Click on one list item
|
||||
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// The header is not clicked
|
||||
if (position > 0) {
|
||||
// We need to subtract the header view
|
||||
position -= 1;
|
||||
mListView.setOnItemClickListener((parent, view, position, id) -> {
|
||||
// The header is not clicked
|
||||
if (position > 0) {
|
||||
// We need to subtract the header view
|
||||
position -= 1;
|
||||
|
||||
// Gets the selected track
|
||||
MusicTrack musicTrack = mMusicTrackAdapter.getItem(position);
|
||||
// Gets the selected track
|
||||
MusicTrack musicTrack = mMusicTrackAdapter.getItem(position);
|
||||
|
||||
// Toggle the track
|
||||
selectTrack(musicTrack, view, TrackSelectionState.Toggle);
|
||||
}
|
||||
// Toggle the track
|
||||
selectTrack(musicTrack, view, TrackSelectionState.Toggle);
|
||||
}
|
||||
});
|
||||
|
||||
// The floating action button
|
||||
mFloatingButtonExport = (FloatingActionButton)rootView.findViewById(R.id.floating_button_export);
|
||||
mFloatingButtonExport.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Export all selected tracks
|
||||
for(SelectedTrack selectedTrack : SelectedTrackList.getInstance().getSelectedItems()) {
|
||||
selectedTrack.export(getActivity());
|
||||
}
|
||||
|
||||
// Clear the selection
|
||||
SelectedTrackList.getInstance().clear(true);
|
||||
mFloatingButtonExport = (FloatingActionButton) rootView.findViewById(R.id.floating_button_export);
|
||||
mFloatingButtonExport.setOnClickListener(v -> {
|
||||
m_CPULock.acquire();
|
||||
// Export all selected tracks
|
||||
for (SelectedTrack selectedTrack : SelectedTrackList.getInstance().getSelectedItems()) {
|
||||
selectedTrack.export(getActivity());
|
||||
}
|
||||
|
||||
if ( m_CPULock.isHeld())
|
||||
{
|
||||
m_CPULock.release();
|
||||
}
|
||||
|
||||
// Clear the selection
|
||||
SelectedTrackList.getInstance().clear(true);
|
||||
});
|
||||
updateFloatingButton();
|
||||
}
|
||||
|
@ -231,33 +233,37 @@ public class MusicTrackListFragment extends Fragment {
|
|||
return rootView;
|
||||
}
|
||||
|
||||
private enum TrackSelectionState { Deselect, Select, Toggle }
|
||||
private enum TrackSelectionState {Deselect, Select, Toggle}
|
||||
|
||||
/**
|
||||
* Select a track
|
||||
*
|
||||
* @param musicTrack The track
|
||||
* @param view The view
|
||||
* @param state Selection state
|
||||
* @param view The view
|
||||
* @param state Selection state
|
||||
*/
|
||||
private void selectTrack(MusicTrack musicTrack, View view, TrackSelectionState state) {
|
||||
// Track is available
|
||||
if (musicTrack.isOfflineAvailable()) {
|
||||
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
PlayMusicExporterPreferences.init(getContext());
|
||||
|
||||
// Default structure
|
||||
String pathStructure = preferences.getString("preference_structure_alba", "{album-artist}/{album}/{disc=CD $}/{no=$$.} {title}.mp3");
|
||||
//Creating Variables
|
||||
String pathStructure;
|
||||
Uri uri;
|
||||
|
||||
// Track is exported from a group (playlist or artist)
|
||||
if (!TextUtils.isEmpty(musicTrack.getContainerName())) {
|
||||
pathStructure = preferences.getString("preference_structure_groups", "{group}/{group-no=$$.} {artist} - {title}.mp3");
|
||||
if (TextUtils.isEmpty(musicTrack.getContainerName())) {
|
||||
pathStructure = PlayMusicExporterPreferences.getAlbaExportStructure();
|
||||
uri = PlayMusicExporterPreferences.getAlbaExportPath();
|
||||
} else {
|
||||
pathStructure = PlayMusicExporterPreferences.getGroupsExportStructure();
|
||||
uri = PlayMusicExporterPreferences.getGroupsExportPath();
|
||||
}
|
||||
|
||||
// Build the path
|
||||
String path = MusicPathBuilder.Build(musicTrack, pathStructure);
|
||||
|
||||
// Gets the root uri
|
||||
Uri uri = Uri.parse(preferences.getString("preference_export_tree_uri", Uri.EMPTY.toString()));
|
||||
|
||||
|
||||
// Prevent the closing
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
package re.jcg.playmusicexporter.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PorterDuff;
|
||||
|
@ -44,7 +44,7 @@ import android.widget.Button;
|
|||
|
||||
import re.jcg.playmusicexporter.R;
|
||||
import re.jcg.playmusicexporter.activities.SettingsActivity;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterSettings;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
|
||||
|
||||
/**
|
||||
* Fragment used for managing interactions for and presentation of a navigation drawer.
|
||||
|
@ -74,7 +74,15 @@ public class NavigationDrawerFragment extends Fragment {
|
|||
private Button mButtonSettings;
|
||||
|
||||
public enum ViewType {
|
||||
Album, Artist, Playlist, Rated
|
||||
Album, Artist, Playlist, Rated;
|
||||
|
||||
public static ViewType fromName(String name) {
|
||||
for (ViewType type : values()) {
|
||||
if (type.name().equals(name)) return type;
|
||||
}
|
||||
return Album;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ViewType mViewType;
|
||||
|
@ -103,8 +111,8 @@ public class NavigationDrawerFragment extends Fragment {
|
|||
mViewType = viewType;
|
||||
|
||||
// Save the selection
|
||||
PlayMusicExporterSettings appSettings = new PlayMusicExporterSettings(getActivity());
|
||||
appSettings.setEnum(PlayMusicExporterSettings.PREF_DRAWER_SELECTED_TYPE, viewType);
|
||||
PlayMusicExporterPreferences.init(getContext());
|
||||
PlayMusicExporterPreferences.setDrawerViewType(viewType);
|
||||
|
||||
// Close the drawer
|
||||
if (mDrawerLayout != null)
|
||||
|
@ -119,9 +127,9 @@ public class NavigationDrawerFragment extends Fragment {
|
|||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Load the settings
|
||||
PlayMusicExporterSettings appSettings = new PlayMusicExporterSettings(getActivity());
|
||||
mUserLearnedDrawer = appSettings.getBoolean(PlayMusicExporterSettings.PREF_DRAWER_LEARNED, false);
|
||||
mViewType = appSettings.getEnum(PlayMusicExporterSettings.PREF_DRAWER_SELECTED_TYPE, ViewType.Album);
|
||||
PlayMusicExporterPreferences.init(getContext());
|
||||
mUserLearnedDrawer = PlayMusicExporterPreferences.getDrawerLearned();
|
||||
mViewType = PlayMusicExporterPreferences.getDrawerViewType();
|
||||
|
||||
|
||||
}
|
||||
|
@ -140,58 +148,35 @@ public class NavigationDrawerFragment extends Fragment {
|
|||
R.layout.fragment_navigation_drawer, container, false);
|
||||
|
||||
// Gets all buttons
|
||||
mButtonTypeAlbum = (Button)view.findViewById(R.id.button_type_album);
|
||||
mButtonTypeArtist = (Button)view.findViewById(R.id.button_type_artist);
|
||||
mButtonTypePlaylist = (Button)view.findViewById(R.id.button_type_playlist);
|
||||
mButtonTypeRated = (Button)view.findViewById(R.id.button_type_rated);
|
||||
mButtonTypeAlbum = (Button) view.findViewById(R.id.button_type_album);
|
||||
mButtonTypeArtist = (Button) view.findViewById(R.id.button_type_artist);
|
||||
mButtonTypePlaylist = (Button) view.findViewById(R.id.button_type_playlist);
|
||||
mButtonTypeRated = (Button) view.findViewById(R.id.button_type_rated);
|
||||
|
||||
mButtonSettings = (Button)view.findViewById(R.id.button_setting);
|
||||
mButtonSettings = (Button) view.findViewById(R.id.button_setting);
|
||||
|
||||
// Set the default
|
||||
setViewType(mViewType);
|
||||
|
||||
// Click on album
|
||||
mButtonTypeAlbum.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setViewType(ViewType.Album);
|
||||
}
|
||||
});
|
||||
mButtonTypeAlbum.setOnClickListener(v -> setViewType(ViewType.Album));
|
||||
|
||||
// Click on artist
|
||||
mButtonTypeArtist.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setViewType(ViewType.Artist);
|
||||
}
|
||||
});
|
||||
mButtonTypeArtist.setOnClickListener(v -> setViewType(ViewType.Artist));
|
||||
|
||||
// Click on playlist
|
||||
mButtonTypePlaylist.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setViewType(ViewType.Playlist);
|
||||
}
|
||||
});
|
||||
mButtonTypePlaylist.setOnClickListener(v -> setViewType(ViewType.Playlist));
|
||||
|
||||
// Click on rated
|
||||
mButtonTypeRated.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setViewType(ViewType.Rated);
|
||||
}
|
||||
});
|
||||
mButtonTypeRated.setOnClickListener(v -> setViewType(ViewType.Rated));
|
||||
|
||||
// Click on settings
|
||||
mButtonSettings.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intentSettings = new Intent(getActivity(), SettingsActivity.class);
|
||||
startActivity(intentSettings);
|
||||
mButtonSettings.setOnClickListener(v -> {
|
||||
Intent intentSettings = new Intent(getActivity(), SettingsActivity.class);
|
||||
startActivity(intentSettings);
|
||||
|
||||
// Close the drawer
|
||||
mDrawerLayout.closeDrawers();
|
||||
}
|
||||
// Close the drawer
|
||||
mDrawerLayout.closeDrawers();
|
||||
});
|
||||
|
||||
// Color the settings button
|
||||
|
@ -202,6 +187,7 @@ public class NavigationDrawerFragment extends Fragment {
|
|||
|
||||
/**
|
||||
* Format the button
|
||||
*
|
||||
* @param button The button
|
||||
* @param active Active
|
||||
*/
|
||||
|
@ -282,8 +268,8 @@ public class NavigationDrawerFragment extends Fragment {
|
|||
// The user manually opened the drawer; store this flag to prevent auto-showing
|
||||
// the navigation drawer automatically in the future.
|
||||
mUserLearnedDrawer = true;
|
||||
PlayMusicExporterSettings appSettings = new PlayMusicExporterSettings(getActivity());
|
||||
appSettings.setBoolean(PlayMusicExporterSettings.PREF_DRAWER_LEARNED, mUserLearnedDrawer);
|
||||
PlayMusicExporterPreferences.init(getContext());
|
||||
PlayMusicExporterPreferences.setDrawerLearned(mUserLearnedDrawer);
|
||||
}
|
||||
|
||||
getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu()
|
||||
|
@ -297,21 +283,16 @@ public class NavigationDrawerFragment extends Fragment {
|
|||
}
|
||||
|
||||
// Defer code dependent on restoration of previous instance state.
|
||||
mDrawerLayout.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDrawerToggle.syncState();
|
||||
}
|
||||
});
|
||||
mDrawerLayout.post(mDrawerToggle::syncState);
|
||||
|
||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
try {
|
||||
mCallbacks = (NavigationDrawerCallbacks) activity;
|
||||
mCallbacks = (NavigationDrawerCallbacks) context;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
|
||||
}
|
||||
|
|
|
@ -6,49 +6,49 @@ import android.app.job.JobScheduler;
|
|||
import android.app.job.JobService;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.util.Log;
|
||||
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
|
||||
|
||||
public class ExportAllJob extends JobService {
|
||||
public static final String TAG = "AutoGPME_ExportJob";
|
||||
public static final int AUTO_EXPORT_JOB_ID = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Schedules an export with the current settings
|
||||
*
|
||||
* @param pContext
|
||||
*/
|
||||
public static void scheduleExport(final Context pContext) {
|
||||
SharedPreferences lPreferences = PreferenceManager.getDefaultSharedPreferences(pContext);
|
||||
lPreferences.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
scheduleExport(pContext);
|
||||
Log.i(TAG, "Preference changed: " + key);
|
||||
}
|
||||
});
|
||||
|
||||
long lInterval = Long.parseLong(lPreferences.getString("settings_export_frequency", "86400000"));
|
||||
boolean lRequireUnmeteredNetwork = lPreferences.getBoolean("settings_export_", false);
|
||||
boolean lRequireCharging = lPreferences.getBoolean("settings", true);
|
||||
|
||||
PlayMusicExporterPreferences.init(pContext);
|
||||
JobScheduler lJobScheduler = (JobScheduler) pContext.getSystemService(JOB_SCHEDULER_SERVICE);
|
||||
if (lInterval == -1) {
|
||||
lJobScheduler.cancel(42);
|
||||
} else {
|
||||
if (PlayMusicExporterPreferences.getAutoExportEnabled()) {
|
||||
long lInterval = PlayMusicExporterPreferences.getAutoExportFrequency();
|
||||
boolean lRequireUnmeteredNetwork = PlayMusicExporterPreferences.getAutoExportRequireUnmetered();
|
||||
boolean lRequireCharging = PlayMusicExporterPreferences.getAutoExportRequireCharging();
|
||||
ComponentName lComponentName = new ComponentName(pContext, ExportAllJob.class);
|
||||
JobInfo.Builder lBuilder = new JobInfo.Builder(42, lComponentName);
|
||||
JobInfo.Builder lBuilder = new JobInfo.Builder(AUTO_EXPORT_JOB_ID, lComponentName);
|
||||
lBuilder.setPeriodic(lInterval);
|
||||
lBuilder.setPersisted(true);
|
||||
if (lRequireUnmeteredNetwork)
|
||||
lBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
|
||||
lBuilder.setRequiresCharging(lRequireCharging);
|
||||
lJobScheduler.schedule(lBuilder.build());
|
||||
} else {
|
||||
lJobScheduler.cancel(AUTO_EXPORT_JOB_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
PlayMusicExporterPreferences.init(this.getApplicationContext());
|
||||
Log.i(TAG, "Started Job: " + params.toString());
|
||||
ExportAllService.startExport(this);
|
||||
if (PlayMusicExporterPreferences.getAutoExportEnabled()) {
|
||||
ExportAllService.startExport(this);
|
||||
} else {
|
||||
scheduleExport(getApplicationContext());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@ package re.jcg.playmusicexporter.services;
|
|||
import android.app.IntentService;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.provider.DocumentFile;
|
||||
import android.os.PowerManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ly.count.android.sdk.Countly;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
|
||||
import re.jcg.playmusicexporter.utils.MusicPathBuilder;
|
||||
import de.arcus.playmusiclib.PlayMusicManager;
|
||||
import de.arcus.playmusiclib.datasources.AlbumDataSource;
|
||||
|
@ -25,8 +25,10 @@ public class ExportAllService extends IntentService {
|
|||
public static final String TAG = "AutoGPME_ExportService";
|
||||
public static final String ACTION_EXPORT = "re.jcg.playmusicexporter.action.EXPORT";
|
||||
public static final String ACTION_SET_EXPORT_JOB = "re.jcg.playmusicexporter.action.SET_EXPORT_JOB";
|
||||
private static PowerManager m_powerManager;
|
||||
|
||||
public static void startExport(Context pContext) {
|
||||
m_powerManager = (PowerManager) pContext.getSystemService(POWER_SERVICE);
|
||||
Intent lIntent = new Intent(pContext, ExportAllService.class);
|
||||
lIntent.setAction(ACTION_EXPORT);
|
||||
pContext.startService(lIntent);
|
||||
|
@ -50,7 +52,7 @@ public class ExportAllService extends IntentService {
|
|||
}
|
||||
|
||||
private void export() {
|
||||
SharedPreferences lPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
PlayMusicExporterPreferences.init(this);
|
||||
PlayMusicManager lPlayMusicManager = new PlayMusicManager(this);
|
||||
|
||||
try {
|
||||
|
@ -58,42 +60,47 @@ public class ExportAllService extends IntentService {
|
|||
} catch (PlayMusicNotFoundException | NoSuperUserException | CouldNotOpenDatabaseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String lStringUri = lPreferences.getString("preference_export_tree_uri", null);
|
||||
String lExportStructure = lPreferences.getString("preference_structure_alba", "{album-artist}/{album}/{disc=CD $}/{no=$$.} {title}.mp3");
|
||||
Log.i(TAG, lStringUri);
|
||||
Uri lUri = Uri.parse(lStringUri);
|
||||
Uri lUri = PlayMusicExporterPreferences.getConditionedAutoExportPath();
|
||||
String lExportStructure = PlayMusicExporterPreferences.getConditionedAutoExportStructure();
|
||||
Log.i(TAG, lUri.toString());
|
||||
AlbumDataSource lAlbumDataSource = new AlbumDataSource(lPlayMusicManager);
|
||||
lAlbumDataSource.setOfflineOnly(true);
|
||||
PowerManager.WakeLock CPULock = m_powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExportAllService");
|
||||
CPULock.acquire();
|
||||
List<Album> lAlba = lAlbumDataSource.getAll();
|
||||
for (Album lAlbum : lAlba) {
|
||||
for (MusicTrack lTrack : lAlbum.getMusicTrackList()) {
|
||||
if (lTrack.isOfflineAvailable()) {
|
||||
String lPath = MusicPathBuilder.Build(lTrack, lExportStructure);
|
||||
if (!isAlreadyThere(lUri, lPath)) {
|
||||
if (lPlayMusicManager.exportMusicTrack(lTrack, lUri, lPath)) {
|
||||
try {
|
||||
if (lPlayMusicManager.exportMusicTrack(lTrack, lUri, lPath, PlayMusicExporterPreferences.getFileOverwritePreference())) {
|
||||
Log.i(TAG, "Exported Music Track: " + getStringForTrack(lTrack));
|
||||
if (PlayMusicExporterPreferences.getReportStats())
|
||||
Countly.sharedInstance().recordEvent("Exported Song", 1);
|
||||
} else {
|
||||
Log.i(TAG, "Failed to export Music Track: " + getStringForTrack(lTrack));
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, lPath + " already exists.");
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (e.getMessage().contains("Invalid URI:")) {
|
||||
/*
|
||||
TODO: Make it impossible to reach this point
|
||||
This happens when the user has not yet set the export path.
|
||||
|
||||
*/
|
||||
Log.i(TAG, "Automatic export failed, because the URI is invalid.");
|
||||
} else throw e;
|
||||
} catch (Exception e) {
|
||||
if (PlayMusicExporterPreferences.getReportStats())
|
||||
Countly.sharedInstance().logException(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAlreadyThere(Uri pUri, String pPath) {
|
||||
DocumentFile lDocumentFile = DocumentFile.fromTreeUri(this, pUri);
|
||||
for (String lDisplayName: pPath.split("/")) {
|
||||
if (lDocumentFile.findFile(lDisplayName) != null) {
|
||||
lDocumentFile = lDocumentFile.findFile(lDisplayName);
|
||||
} else {
|
||||
Log.i(TAG, pPath + " does not exist yet.");
|
||||
return false;
|
||||
}
|
||||
if (CPULock.isHeld()) {
|
||||
CPULock.release();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getStringForTrack(MusicTrack pTrack) {
|
||||
|
|
|
@ -31,10 +31,12 @@ import android.os.Bundle;
|
|||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import de.arcus.framework.logger.Logger;
|
||||
import ly.count.android.sdk.Countly;
|
||||
import re.jcg.playmusicexporter.R;
|
||||
import de.arcus.playmusiclib.PlayMusicManager;
|
||||
import de.arcus.playmusiclib.datasources.MusicTrackDataSource;
|
||||
import de.arcus.playmusiclib.items.MusicTrack;
|
||||
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
|
||||
|
||||
/**
|
||||
* The export service
|
||||
|
@ -199,9 +201,18 @@ public class ExportService extends IntentService {
|
|||
updateNotification();
|
||||
|
||||
// Exports the song
|
||||
if(!playMusicManager.exportMusicTrack(mTrackCurrent, uri, path)) {
|
||||
// Export failed
|
||||
mTracksFailed ++;
|
||||
try {
|
||||
if (playMusicManager.exportMusicTrack(mTrackCurrent, uri, path, PlayMusicExporterPreferences.getFileOverwritePreference())) {
|
||||
if (PlayMusicExporterPreferences.getReportStats())
|
||||
Countly.sharedInstance().recordEvent("Exported Song", 1);
|
||||
} else {
|
||||
// Export failed
|
||||
mTracksFailed ++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (PlayMusicExporterPreferences.getReportStats())
|
||||
Countly.sharedInstance().logException(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
// Export failed
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
package re.jcg.playmusicexporter.settings;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import re.jcg.playmusicexporter.BuildConfig;
|
||||
import re.jcg.playmusicexporter.fragments.NavigationDrawerFragment;
|
||||
|
||||
public class PlayMusicExporterPreferences {
|
||||
private static SharedPreferences preferences;
|
||||
|
||||
public static final String AUTO_EXPORT_ENABLED = "preference_auto_export_enabled";
|
||||
public static final boolean AUTO_EXPORT_ENABLED_DEFAULT = false;
|
||||
public static final String AUTO_EXPORT_USES_DIFFERENT_PATH = "preference_auto_export_use_different_path";
|
||||
public static final boolean AUTO_EXPORT_USES_DIFFERENT_PATH_DEFAULT = false;
|
||||
private static final String FORCE_EXPORT_OVERWRITE = "preference_overwrite_existing_files";
|
||||
private static final boolean FORCE_EXPORT_OVERWRITE_DEFAULT = false;
|
||||
public static final String AUTO_EXPORT_USES_DIFFERENT_STRUCTURE = "preference_auto_export_use_different_structure";
|
||||
public static final boolean AUTO_EXPORT_USES_DIFFERENT_STRUCTURE_DEFAULT = false;
|
||||
public static final String AUTO_EXPORT_FREQUENCY = "preference_auto_export_frequency";
|
||||
public static final String AUTO_EXPORT_FREQUENCY_DEFAULT = "86400000";
|
||||
public static final String AUTO_EXPORT_REQUIRE_CHARGING = "preference_auto_export_require_charging";
|
||||
public static final String AUTO_EXPORT_REQUIRE_UNMETERED = "preference_auto_export_require_unmetered";
|
||||
public static final boolean AUTO_EXPORT_REQUIRE_CONDITION_DEFAULT = false;
|
||||
|
||||
public static final String AUTO_EXPORT_PATH = "preference_auto_export_path";
|
||||
//TODO Split Export Paths in export prefs, this won't work else.
|
||||
public static final String ALBA_EXPORT_PATH = "preference_alba_export_path";
|
||||
public static final String GROUPS_EXPORT_PATH = "preference_groups_export_path";
|
||||
public static final String URI_DEFAULT = Uri.fromFile(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)).toString();
|
||||
|
||||
public static final String AUTO_EXPORT_STRUCTURE = "preference_auto_export_structure";
|
||||
public static final String ALBA_EXPORT_STRUCTURE = "preference_alba_export_structure";
|
||||
public static final String GROUPS_EXPORT_STRUCTURE = "preference_groups_export_structure";
|
||||
public static final String EXPORT_STRUCTURE_DEFAULT = "{album-artist}/{album}/{disc=CD $}/{no=$$.} {title}.mp3";
|
||||
|
||||
public static final String EXPORT_ALBUM_ART_SIZE = "preference_id3_artwork_size";
|
||||
public static final int EXPORT_ALBUM_ART_SIZE_DEFAULT = 512;
|
||||
|
||||
public static final String DRAWER_LEARNED = "pref_drawer_learned";
|
||||
public static final boolean DRAWER_LEARNED_DEFAULT = false;
|
||||
public static final String DRAWER_SELECTED_TYPE = "pref_drawer_selected_type";
|
||||
public static final String DRAWER_SELECTED_TYPE_DEFAULT = "Album";
|
||||
|
||||
public static final String SETUP_DONE = "preference_setup_done";
|
||||
public static final boolean SETUP_DONE_DEFAULT = false;
|
||||
|
||||
public static final String REPORT_STATS = "preference_report_stats";
|
||||
public static final boolean REPORT_STATS_DEFAULT = true;
|
||||
|
||||
|
||||
private PlayMusicExporterPreferences() {
|
||||
}
|
||||
|
||||
public static void init(Context pContext) {
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(pContext);
|
||||
}
|
||||
|
||||
public static Uri getConditionedAutoExportPath() {
|
||||
if (getAutoExportUsesDifferentPath()) {
|
||||
return getAutoExportPath();
|
||||
} else {
|
||||
return getAlbaExportPath();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getFileOverwritePreference() {
|
||||
return preferences.getBoolean(FORCE_EXPORT_OVERWRITE, FORCE_EXPORT_OVERWRITE_DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean getAutoExportUsesDifferentPath() {
|
||||
return preferences.getBoolean(AUTO_EXPORT_USES_DIFFERENT_PATH, AUTO_EXPORT_USES_DIFFERENT_PATH_DEFAULT);
|
||||
}
|
||||
|
||||
public static Uri getAutoExportPath() {
|
||||
return getUri(AUTO_EXPORT_PATH);
|
||||
}
|
||||
|
||||
public static Uri getAlbaExportPath() {
|
||||
return getUri(ALBA_EXPORT_PATH);
|
||||
}
|
||||
|
||||
public static Uri getGroupsExportPath() {
|
||||
return getUri(GROUPS_EXPORT_PATH);
|
||||
}
|
||||
|
||||
private static Uri getUri(String key) {
|
||||
return Uri.parse(preferences.getString(key, URI_DEFAULT));
|
||||
}
|
||||
|
||||
public static String getConditionedAutoExportStructure() {
|
||||
if (getAutoExportUsesDifferentStructure()) {
|
||||
return getAutoExportStructure();
|
||||
} else {
|
||||
return getAlbaExportStructure();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAlbaExportStructure() {
|
||||
return preferences.getString(ALBA_EXPORT_STRUCTURE, EXPORT_STRUCTURE_DEFAULT);
|
||||
}
|
||||
|
||||
public static String getGroupsExportStructure() {
|
||||
return preferences.getString(GROUPS_EXPORT_STRUCTURE, EXPORT_STRUCTURE_DEFAULT);
|
||||
}
|
||||
|
||||
public static String getAutoExportStructure() {
|
||||
return preferences.getString(AUTO_EXPORT_STRUCTURE, EXPORT_STRUCTURE_DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean getAutoExportUsesDifferentStructure() {
|
||||
return preferences.getBoolean(AUTO_EXPORT_USES_DIFFERENT_STRUCTURE, AUTO_EXPORT_USES_DIFFERENT_STRUCTURE_DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean getDrawerLearned() {
|
||||
return preferences.getBoolean(DRAWER_LEARNED, DRAWER_LEARNED_DEFAULT);
|
||||
}
|
||||
public static void setDrawerLearned(boolean drawerLearned) {
|
||||
preferences.edit().putBoolean(DRAWER_LEARNED, drawerLearned).apply();
|
||||
}
|
||||
|
||||
public static NavigationDrawerFragment.ViewType getDrawerViewType() {
|
||||
return NavigationDrawerFragment.ViewType.fromName(preferences.getString(DRAWER_SELECTED_TYPE, DRAWER_SELECTED_TYPE_DEFAULT));
|
||||
}
|
||||
|
||||
public static void setDrawerViewType(NavigationDrawerFragment.ViewType viewType) {
|
||||
preferences.edit().putString(DRAWER_SELECTED_TYPE, viewType.name()).apply();
|
||||
}
|
||||
|
||||
public static void setAlbaExportPath(Uri treeUri) {
|
||||
preferences.edit().putString(ALBA_EXPORT_PATH, treeUri.toString()).apply();
|
||||
}
|
||||
|
||||
public static void setGroupsExportPath(Uri treeUri) {
|
||||
preferences.edit().putString(GROUPS_EXPORT_PATH, treeUri.toString()).apply();
|
||||
}
|
||||
|
||||
public static boolean getAutoExportEnabled() {
|
||||
return preferences.getBoolean(AUTO_EXPORT_ENABLED, AUTO_EXPORT_ENABLED_DEFAULT);
|
||||
}
|
||||
|
||||
public static void registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener) {
|
||||
preferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
public static long getAutoExportFrequency() {
|
||||
return Long.parseLong(preferences.getString(AUTO_EXPORT_FREQUENCY, AUTO_EXPORT_FREQUENCY_DEFAULT));
|
||||
}
|
||||
|
||||
public static boolean getAutoExportRequireUnmetered() {
|
||||
return preferences.getBoolean(AUTO_EXPORT_REQUIRE_UNMETERED, AUTO_EXPORT_REQUIRE_CONDITION_DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean getAutoExportRequireCharging() {
|
||||
return preferences.getBoolean(AUTO_EXPORT_REQUIRE_CHARGING, AUTO_EXPORT_REQUIRE_CONDITION_DEFAULT);
|
||||
}
|
||||
|
||||
public static void setAutoExportPath(Uri treeUri) {
|
||||
preferences.edit().putString(AUTO_EXPORT_PATH, treeUri.toString()).apply();
|
||||
}
|
||||
|
||||
public static boolean getSetupDone() {
|
||||
return preferences.getBoolean(SETUP_DONE, SETUP_DONE_DEFAULT);
|
||||
}
|
||||
|
||||
public static void setSetupDone(boolean done) {
|
||||
preferences.edit().putBoolean(SETUP_DONE, done).apply();
|
||||
}
|
||||
|
||||
public static int getAlbumArtSize() {
|
||||
return Integer.parseInt(preferences.getString(EXPORT_ALBUM_ART_SIZE, "" + EXPORT_ALBUM_ART_SIZE_DEFAULT));
|
||||
}
|
||||
|
||||
public static void setAlbumArtSize(int size) {
|
||||
preferences.edit().putString(EXPORT_ALBUM_ART_SIZE, "" + size).apply();
|
||||
}
|
||||
|
||||
public static boolean getReportStats() {
|
||||
//Never report stats in debug builds
|
||||
return preferences.getBoolean(REPORT_STATS, REPORT_STATS_DEFAULT) && !BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
public static void setReportStats(boolean reportStats) {
|
||||
preferences.edit().putBoolean(REPORT_STATS, reportStats).apply();
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 David Schulte
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package re.jcg.playmusicexporter.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
|
||||
import de.arcus.framework.settings.AppSettings;
|
||||
import re.jcg.playmusicexporter.R;
|
||||
|
||||
/**
|
||||
* Helper class to read and write app settings without to care about to open and close an editor
|
||||
*/
|
||||
public class PlayMusicExporterSettings extends AppSettings {
|
||||
private static PlayMusicExporterSettings settings;
|
||||
/**
|
||||
* The default settings file
|
||||
*/
|
||||
public static final String DEFAULT_SETTINGS_FILENAME = "play_music_exporter";
|
||||
|
||||
// Preference constants
|
||||
private static final String PREF_ID3 = "pref_id3";
|
||||
private static final String PREF_ID3_ARTWORK_SIZE = "pref_id3_artwork_size";
|
||||
public static final String PREF_EXPORT_URI = "pref_export_uri";
|
||||
private static final String PREF_STRUCTURE_ALBA = "pref_structure_albua";
|
||||
private static final String PREF_STRUCTURE_GROUPS = "pref_structure_groups";
|
||||
public static final String PREF_DRAWER_LEARNED = "pref_drawer_learned";
|
||||
public static final String PREF_DRAWER_SELECTED_TYPE = "pref_drawer_selected_type";
|
||||
|
||||
public PlayMusicExporterSettings(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of PlayMusicExporterSettings that access to the default settings file
|
||||
* @param context Context of the app
|
||||
*/
|
||||
public static void init(Context context) {
|
||||
settings = new PlayMusicExporterSettings(context);
|
||||
|
||||
// Init the default values
|
||||
|
||||
// ID3 settings
|
||||
if (!settings.contains(PREF_ID3))
|
||||
settings.setString(PREF_ID3, "id3_with_cover");
|
||||
|
||||
// ID3 artwork settings
|
||||
if (!settings.contains(PREF_ID3_ARTWORK_SIZE))
|
||||
settings.setInt(PREF_ID3_ARTWORK_SIZE, 1024);
|
||||
|
||||
// Export path
|
||||
if (!settings.contains(PREF_EXPORT_URI))
|
||||
settings.setUri(PREF_EXPORT_URI, Uri.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)));
|
||||
|
||||
// Alba export structure
|
||||
if (!settings.contains(PREF_STRUCTURE_ALBA))
|
||||
settings.setString(PREF_STRUCTURE_ALBA, context.getString(R.string.settings_export_structure_alba_default_value));
|
||||
|
||||
// Groups export structure
|
||||
if (!settings.contains(PREF_STRUCTURE_GROUPS))
|
||||
settings.setString(PREF_STRUCTURE_GROUPS, context.getString(R.string.settings_export_structure_groups_default_value));
|
||||
|
||||
// Drawer learned
|
||||
if (!settings.contains(PREF_DRAWER_LEARNED))
|
||||
settings.setBoolean(PREF_DRAWER_LEARNED, false);
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -22,14 +22,12 @@
|
|||
|
||||
package re.jcg.playmusicexporter.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import re.jcg.playmusicexporter.R;
|
||||
import de.arcus.playmusiclib.ArtworkLoader;
|
||||
import de.arcus.playmusiclib.ArtworkLoaderCallback;
|
||||
import de.arcus.playmusiclib.items.ArtworkEntry;
|
||||
|
||||
/**
|
||||
|
@ -121,13 +119,10 @@ public class ArtworkViewLoader {
|
|||
maximalArtworkSize = imageViewDefault.getContext().getResources().getDimensionPixelSize(R.dimen.music_track_artwork_loading_size);
|
||||
|
||||
// Sets the bitmap in the UI thread
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Default icon
|
||||
imageViewDefault.setImageResource(mDefaultImage);
|
||||
Runnable runnable = () -> {
|
||||
// Default icon
|
||||
imageViewDefault.setImageResource(mDefaultImage);
|
||||
|
||||
}
|
||||
};
|
||||
imageViewDefault.post(runnable);
|
||||
}
|
||||
|
@ -138,36 +133,30 @@ public class ArtworkViewLoader {
|
|||
mIsLoading = true;
|
||||
|
||||
// Load the artwork
|
||||
ArtworkLoader.loadArtworkAsync(mArtworkEntry, maximalArtworkSize, new ArtworkLoaderCallback() {
|
||||
@Override
|
||||
public void onFinished(final Bitmap bitmap) {
|
||||
final ImageView imageView = mImageView.get();
|
||||
ArtworkLoader.loadArtworkAsync(mArtworkEntry, maximalArtworkSize, bitmap -> {
|
||||
final ImageView imageView = mImageView.get();
|
||||
|
||||
if (imageViewDefault != null) {
|
||||
// Sets the bitmap in the UI thread
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Bitmap is valid
|
||||
if (bitmap != null)
|
||||
imageView.setImageBitmap(bitmap);
|
||||
else
|
||||
imageView.setImageResource(mDefaultImage);
|
||||
}
|
||||
};
|
||||
imageView.post(runnable);
|
||||
}
|
||||
if (imageViewDefault != null) {
|
||||
// Sets the bitmap in the UI thread
|
||||
Runnable runnable = () -> {
|
||||
// Bitmap is valid
|
||||
if (bitmap != null)
|
||||
imageView.setImageBitmap(bitmap);
|
||||
else
|
||||
imageView.setImageResource(mDefaultImage);
|
||||
};
|
||||
imageView.post(runnable);
|
||||
}
|
||||
|
||||
// Loading is done
|
||||
mIsLoading = false;
|
||||
// Loading is done
|
||||
mIsLoading = false;
|
||||
|
||||
// Loads the next image
|
||||
if (mNewArtworkEntry != null) {
|
||||
mArtworkEntry = mNewArtworkEntry;
|
||||
mNewArtworkEntry = null;
|
||||
// Loads the next image
|
||||
if (mNewArtworkEntry != null) {
|
||||
mArtworkEntry = mNewArtworkEntry;
|
||||
mNewArtworkEntry = null;
|
||||
|
||||
loadImage();
|
||||
}
|
||||
loadImage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -85,7 +85,8 @@ public class MusicPathBuilder {
|
|||
if (!TextUtils.isEmpty(musicTrack.getAlbum()))
|
||||
value = musicTrack.getAlbum();
|
||||
break;
|
||||
case "group":
|
||||
case "playlist":
|
||||
case "group": //previous name, for compatability
|
||||
if (!TextUtils.isEmpty(musicTrack.getContainerName()))
|
||||
value = musicTrack.getContainerName();
|
||||
break;
|
||||
|
|
Before Width: | Height: | Size: 2.4 KiB |
BIN
playmusicexporter/src/main/res/drawable-hdpi/cd_case.webp
Normal file
After Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 829 B |
BIN
playmusicexporter/src/main/res/drawable-hdpi/cd_overlay.webp
Normal file
After Width: | Height: | Size: 710 B |
Before Width: | Height: | Size: 1.5 KiB |
BIN
playmusicexporter/src/main/res/drawable-mdpi/cd_case.webp
Normal file
After Width: | Height: | Size: 238 B |
Before Width: | Height: | Size: 620 B |
BIN
playmusicexporter/src/main/res/drawable-mdpi/cd_overlay.webp
Normal file
After Width: | Height: | Size: 576 B |
Before Width: | Height: | Size: 390 B |
BIN
playmusicexporter/src/main/res/drawable-mdpi/ic_type_rated.webp
Normal file
After Width: | Height: | Size: 388 B |
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015 David Schulte
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in
|
||||
~ all copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
~ THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/button_navigation_drawer_hover">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/button_navigation_drawer_normal"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015 David Schulte
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in
|
||||
~ all copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
~ THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/button_navigation_drawer_hover">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/button_navigation_drawer_selected"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
Before Width: | Height: | Size: 3.4 KiB |
BIN
playmusicexporter/src/main/res/drawable-xhdpi/cd_case.webp
Normal file
After Width: | Height: | Size: 522 B |
Before Width: | Height: | Size: 1.1 KiB |
BIN
playmusicexporter/src/main/res/drawable-xhdpi/cd_overlay.webp
Normal file
After Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 470 B |
After Width: | Height: | Size: 440 B |
Before Width: | Height: | Size: 642 B |
BIN
playmusicexporter/src/main/res/drawable-xhdpi/ic_type_rated.webp
Normal file
After Width: | Height: | Size: 562 B |
Before Width: | Height: | Size: 5.6 KiB |
BIN
playmusicexporter/src/main/res/drawable-xxhdpi/cd_case.webp
Normal file
After Width: | Height: | Size: 822 B |
Before Width: | Height: | Size: 1.6 KiB |
BIN
playmusicexporter/src/main/res/drawable-xxhdpi/cd_overlay.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 574 B |
After Width: | Height: | Size: 514 B |
Before Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 856 B |
Before Width: | Height: | Size: 431 B |
After Width: | Height: | Size: 370 B |
Before Width: | Height: | Size: 877 B |
After Width: | Height: | Size: 742 B |
|
@ -21,7 +21,11 @@
|
|||
~ THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/button_navigation_drawer_normal"/>
|
||||
</shape>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/button_navigation_drawer_hover">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/button_navigation_drawer_normal"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
|
@ -21,7 +21,11 @@
|
|||
~ THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/button_navigation_drawer_selected"/>
|
||||
</shape>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/button_navigation_drawer_hover">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/button_navigation_drawer_selected"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="240dp"
|
||||
android:height="240dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
|
||||
</vector>
|
13
playmusicexporter/src/main/res/drawable/ic_folder_white.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<vector android:height="240dp" android:viewportHeight="1000.0"
|
||||
android:viewportWidth="1000.0" android:width="240dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#ffffff"
|
||||
android:pathData="M110.9,67.5C79.5,70.9 48.7,97.3 37,131.2L33.4,141.5l0,302.6 0,302.6l2.6,7.9c11.4,34.5 43.1,61.5 76.4,65 12.9,1.4 764.6,1.4 777.5,0 35.7,-3.8 68.4,-33.6 78,-71.3 1.4,-5.4 1.6,-43.3 1.3,-260.3L968.9,234.1l-3.1,-9c-8.9,-26.4 -34.1,-51.7 -60.4,-60.7L898.2,161.8 700.5,161.5 502.8,161.2 455.6,113.8 408.5,66.4 263.6,66.6C183.9,66.6 115.2,67.1 110.9,67.5" android:strokeColor="#00000000"/>
|
||||
<path android:fillColor="#ffffff"
|
||||
android:pathData="M110.9,67.5C79.5,70.9 48.7,97.3 37,131.2L33.4,141.5l0,302.6 0,302.6l2.6,7.9c11.4,34.5 43.1,61.5 76.4,65 12.9,1.4 764.6,1.4 777.5,0 35.7,-3.8 68.4,-33.6 78,-71.3 1.4,-5.4 1.6,-43.3 1.3,-260.3L968.9,234.1l-3.1,-9c-8.9,-26.4 -34.1,-51.7 -60.4,-60.7L898.2,161.8 700.5,161.5 502.8,161.2 455.6,113.8 408.5,66.4 263.6,66.6C183.9,66.6 115.2,67.1 110.9,67.5" android:strokeColor="#00000000"/>
|
||||
<path android:fillColor="#ffffff"
|
||||
android:pathData="M110.9,67.5C79.5,70.9 48.7,97.3 37,131.2L33.4,141.5l0,302.6 0,302.6l2.6,7.9c11.4,34.5 43.1,61.5 76.4,65 12.9,1.4 764.6,1.4 777.5,0 35.7,-3.8 68.4,-33.6 78,-71.3 1.4,-5.4 1.6,-43.3 1.3,-260.3L968.9,234.1l-3.1,-9c-8.9,-26.4 -34.1,-51.7 -60.4,-60.7L898.2,161.8 700.5,161.5 502.8,161.2 455.6,113.8 408.5,66.4 263.6,66.6C183.9,66.6 115.2,67.1 110.9,67.5" android:strokeColor="#00000000"/>
|
||||
<path android:fillColor="#ffffff"
|
||||
android:pathData="M110.9,67.5C79.5,70.9 48.7,97.3 37,131.2L33.4,141.5l0,302.6 0,302.6l2.6,7.9c11.4,34.5 43.1,61.5 76.4,65 12.9,1.4 764.6,1.4 777.5,0 35.7,-3.8 68.4,-33.6 78,-71.3 1.4,-5.4 1.6,-43.3 1.3,-260.3L968.9,234.1l-3.1,-9c-8.9,-26.4 -34.1,-51.7 -60.4,-60.7L898.2,161.8 700.5,161.5 502.8,161.2 455.6,113.8 408.5,66.4 263.6,66.6C183.9,66.6 115.2,67.1 110.9,67.5" android:strokeColor="#00000000"/>
|
||||
<path android:fillColor="#ffffff"
|
||||
android:pathData="M110.9,67.5C79.5,70.9 48.7,97.3 37,131.2L33.4,141.5l0,302.6 0,302.6l2.6,7.9c11.4,34.5 43.1,61.5 76.4,65 12.9,1.4 764.6,1.4 777.5,0 35.7,-3.8 68.4,-33.6 78,-71.3 1.4,-5.4 1.6,-43.3 1.3,-260.3L968.9,234.1l-3.1,-9c-8.9,-26.4 -34.1,-51.7 -60.4,-60.7L898.2,161.8 700.5,161.5 502.8,161.2 455.6,113.8 408.5,66.4 263.6,66.6C183.9,66.6 115.2,67.1 110.9,67.5" android:strokeColor="#00000000"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="240dp" android:viewportHeight="2540.0"
|
||||
android:viewportWidth="2440.0" android:width="240dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#ffffff"
|
||||
android:pathData="M422,2416c-11,-15 -26,-64 -37,-128 -10,-57 -20,-109 -22,-115 -4,-9 -27,-13 -68,-13 -38,0 -66,-5 -72,-12 -6,-7 -40,-173 -77,-368l-68,-355 5,-160c3,-124 9,-179 27,-245 109,-407 426,-724 835,-831 79,-21 112,-24 275,-24 163,0 196,3 275,24 409,107 726,424 835,831 18,66 24,121 27,245l5,160 -68,355c-37,195 -71,361 -77,368 -6,7 -34,12 -72,12 -41,0 -64,4 -68,13 -2,6 -12,58 -22,115 -28,159 -16,152 -257,152 -200,0 -201,0 -235,-26 -70,-54 -75,-91 -34,-299 10,-55 29,-152 41,-215 72,-392 79,-414 140,-445 52,-27 102,-18 268,46 86,34 165,68 174,77 15,12 18,12 22,1 8,-26 25,-134 32,-201l6,-68 -90,0c-83,0 -93,-2 -121,-26 -17,-14 -31,-35 -31,-45 0,-49 -35,-180 -65,-245 -78,-171 -188,-285 -352,-368 -29,-14 -55,-26 -58,-26 -3,0 -5,92 -5,205l0,205 95,0c70,0 95,3 95,13 0,7 -103,115 -229,241l-229,229 -95,-89c-128,-120 -367,-369 -367,-383 0,-7 32,-11 90,-11l90,0 0,-205 0,-205 -52,26c-166,84 -274,197 -353,367 -30,66 -65,197 -65,246 0,10 -14,31 -31,45 -28,24 -38,26 -121,26l-90,0 6,68c7,67 24,175 32,201 4,11 7,11 22,-1 9,-9 88,-43 174,-77 166,-64 216,-73 268,-46 61,31 68,53 140,445 12,63 31,160 41,215 41,208 36,245 -34,299 -34,26 -35,26 -235,26 -200,0 -201,0 -220,-24z" android:strokeColor="#00000000"/>
|
||||
</vector>
|
25
playmusicexporter/src/main/res/drawable/ic_menu_refresh.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
Copyright (C) 2014 The Android Open Source 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:pathData="M17.6,6.4C16.2,4.9 14.2,4 12,4c-4.4,0 -8,3.6 -8,8s3.6,8 8,8c3.7,0 6.8,-2.6 7.7,-6l-2.1,0c-0.8,2.3 -3,4 -5.6,4c-3.3,0 -6,-2.7 -6,-6s2.7,-6 6,-6c1.7,0 3.1,0.7 4.2,1.8L13,11l7,0L20,4L17.6,6.4z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
6
playmusicexporter/src/main/res/drawable/ic_superuser.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<vector android:height="240dp" android:viewportHeight="978.0"
|
||||
android:viewportWidth="956.0" android:width="240dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillAlpha="1" android:fillColor="#FFFFFF"
|
||||
android:pathData="M925.9,377.9L697,377.9L633.8,591.6L845.5,591.6l0,76.7L609.7,668.3L538.3,907.6L449.6,907.6L521.1,668.3L335.5,668.3L264.1,907.6L175.4,907.6L246.9,668.3L42.1,668.3l0,-76.7L270.9,591.6L334.2,377.9L122.5,377.9l0,-76.7L358.2,301.1L430.4,61.2l88.7,0L446.9,301.1l185.6,0L704.6,61.2l88.7,0L721.1,301.1L925.9,301.1ZM610.4,376.7L422.1,376.7l-64.6,216l188.3,0z"
|
||||
android:strokeColor="#00000000" android:strokeWidth="32.00193405"/>
|
||||
</vector>
|
|
@ -0,0 +1,4 @@
|
|||
<vector android:height="240dp" android:viewportHeight="512.0"
|
||||
android:viewportWidth="512.0" android:width="240dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="m507.6,431.9l-224,-384c-5.7,-9.8 -16.3,-15.9 -27.6,-15.9 -11.4,0 -21.9,6 -27.6,15.9l-224,384c-5.8,9.9 -5.8,22.1 -0.1,32.1 5.7,9.9 16.3,16.1 27.8,16.1h448c11.5,0 22,-6.1 27.8,-16.1 5.7,-9.9 5.7,-22.2 -0.1,-32.1zM256,416c-17.7,0 -32,-14.3 -32,-32 0,-17.7 14.3,-32 32,-32 17.7,0 32,14.3 32,32 0,17.7 -14.3,32 -32,32zM288,288c0,17.7 -14.3,32 -32,32s-32,-14.3 -32,-32v-96c0,-17.7 14.3,-32 32,-32s32,14.3 32,32v96z"/>
|
||||
</vector>
|
|
@ -29,4 +29,10 @@
|
|||
app:showAsAction="always"
|
||||
app:actionViewClass="android.support.v7.widget.SearchView"
|
||||
/>
|
||||
|
||||
|
||||
<item android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refesh"
|
||||
android:icon="@drawable/ic_menu_refresh"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
|
@ -22,18 +22,17 @@
|
|||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">Play Music Exporter (beta)</string>
|
||||
<string name="navigation_drawer_close">Drawer schließen</string>
|
||||
<string name="view_type_selection_title">Ansicht</string>
|
||||
<string name="title_view_rated">Positiv bewertet</string>
|
||||
<string name="title_view_playlist">Playlists</string>
|
||||
<string name="title_view_playlist">Playlisten</string>
|
||||
<string name="title_view_artist">Interpreten</string>
|
||||
<string name="title_view_album">Alben</string>
|
||||
<string name="navigation_drawer_open">Drawer öffnen</string>
|
||||
<string name="title_track_detail">Titeldetails</string>
|
||||
<string name="title_settings">Einstellungen</string>
|
||||
<string name="settings_category_about_me">Über mich</string>
|
||||
<string name="settings_category_export">Export-Einstellungen</string>
|
||||
<string name="settings_category_export">Export Einstellungen</string>
|
||||
<string name="settings_category_extra">Extras</string>
|
||||
<string name="settings_category_thanks">Dank an</string>
|
||||
<string name="settings_export_path">Speicherort</string>
|
||||
|
@ -54,15 +53,15 @@
|
|||
<string name="action_share">Teilen</string>
|
||||
<string name="action_select_all">Alles markieren</string>
|
||||
<string name="settings_export_path_custom">Benutzerdefinierter Pfad</string>
|
||||
<string name="settings_export_id3_artwork_size">Maximale Artworkgröße</string>
|
||||
<string name="settings_export_id3_artwork_size">Maximale Covergröße</string>
|
||||
<string name="settings_export_id3_artwork_size_summary">Wenn die Größe des Artworks größer als der eingestellte Wert ist, wird das Artwork auf diesen Wert runter skalliert.</string>
|
||||
<string name="action_mode_track_selection">%d Titel ausgewählt</string>
|
||||
<string name="action_deselect_all">Auswahl aufheben</string>
|
||||
<string name="toast_error_track_not_offline">Sie können nur offline Titel exportieren. Versuchen Sie das Album in Play Music offline herunterzuladen.</string>
|
||||
<string name="settings_build_date">Build date</string>
|
||||
<string name="settings_category_develop">Entwicklung</string>
|
||||
<string name="dialog_storage_access_denied_title">Daten-Zugriff verweigert</string>
|
||||
<string name="dialog_storage_access_denied">Der PlayMusicExpoerter benötigt Schreibrechte auf der SD-Karte. Dies ist Teil des Marshmallow-Fix.\n\nBitte starten Sie die App neu und erlauben Sie die Berechtigung.</string>
|
||||
<string name="toast_error_track_not_offline">Du kannst nur offline Titel exportieren. Versuche das Album in Play Music offline herunterzuladen.</string>
|
||||
<string name="settings_build_date">Kompilationsdatum</string>
|
||||
<string name="settings_category_develop">Über diese Version</string>
|
||||
<string name="dialog_storage_access_denied_title">Speicher-Zugriff verweigert</string>
|
||||
<string name="dialog_storage_access_denied">Der Play Music Exporter benötigt Schreibzugriff auf den externen Speicher, ohne diesen kann die App nicht genutzt werden.</string>
|
||||
<string name="text_okay">OK</string>
|
||||
<string name="pref_header_about">Über</string>
|
||||
<string name="pref_header_automation">Automatisierung</string>
|
||||
|
@ -73,10 +72,62 @@
|
|||
<string name="settings_donation_old_summery">Spende an David Schulte via PayPal</string>
|
||||
<string name="settings_donation_old_title">Unterstützt den ursprünglichen Entwickler</string>
|
||||
<string name="settings_export_structure_alba">Verzeichnisbaum für Alben</string>
|
||||
<string name="settings_export_subdirectory_structure_album">Setze den Verzeichnisbaum</string>
|
||||
<string name="settings_export_structure_alba_dialog_title">Setze den Verzeichnisbaum</string>
|
||||
<string name="settings_export_subdirectory_structure_album_example">Beispiel: Beatles/Help!/13. Yesterday.mp3</string>
|
||||
<string name="settings_export_subdirectory_structure_dialog_message">"Nutze / für einen neuen Order. Available Tags: - {album-artist} - {album} - {artist} - {title} - {disc=CD $} - {no=$$.} - {year} - {genre} "</string>
|
||||
<string name="settings_export_subdirectory_structure_group">Setze den Verzeichnisbaum</string>
|
||||
<string name="settings_export_subdirectory_structure_dialog_message">Nutze / für einen neuen Order \n Available Tags: \n - {album-artist} \n - {album} \n - {artist} \n - {title} \n - {disc=CD $} \n - {no=$$.} \n - {year} \n - {genre} \n</string>
|
||||
<string name="settings_playlist_export_subdirectory_structure_dialog_message">Nutze / für einen neuen Order \n Available Tags: \n - {playlist} \n - {album-artist} \n - {album} \n - {artist} \n - {title} \n - {disc=CD $} \n - {no=$$.} \n - {year} \n - {genre} \n</string>
|
||||
<string name="settings_export_structure_groups_dialog_title">Setze den Verzeichnisbaum</string>
|
||||
<string name="settings_export_subdirectory_structure_group_example">Beispiel: Great Songs/4. Beatles - Yesterday.mp3</string>
|
||||
<string name="settings_version_number">Versionsnummer</string>
|
||||
<string name="debug_trigger_export_all_title">Jetzt den ExportAllService starten</string>
|
||||
<string name="settings_category_debug">Debuggen</string>
|
||||
<string name="pref_header_debug">Debuggen</string>
|
||||
<string name="settings_auto_export_different_path">Export Pfad</string>
|
||||
<string name="settings_auto_export_different_path_switch">Nutze einen anderen Export Pfad</string>
|
||||
<string name="settings_auto_export_different_path_switch_summary">Wenn dieser Schalter aus ist nutzt der automatische Export den gleichen Pfad wie der manuelle Export von Alben</string>
|
||||
<string name="settings_auto_export_different_structure">Verzeichnisbaum</string>
|
||||
<string name="settings_auto_export_different_structure_switch">Nutze einen anderen Verzeichnisbaum</string>
|
||||
<string name="settings_auto_export_different_structure_switch_summary">Wenn dieser Schalter aus ist nutzt der automatische Export den gleichen Verzeichnisbaum wie der manuelle Export von Alben</string>
|
||||
<string name="settings_auto_export_enable_checkbox">Automatischer Export aktiviert</string>
|
||||
<string name="settings_auto_export_interval_list">Export Interval</string>
|
||||
<string name="settings_auto_export_require_charging_checkbox">Setze Laden des Geräts voraus</string>
|
||||
<string name="settings_auto_export_require_unmetered_checkbox">Setze eine unbeschränkte Netzwerkverbindung voraus</string>
|
||||
<string name="settings_category_alba_export">Export Speichereinstellungen für Alben</string>
|
||||
<string name="settings_category_auto_export_conditions">Export Bedingungen</string>
|
||||
<string name="settings_category_auto_export_path_subdir">Export Pfad und Verzeichnisbaum</string>
|
||||
<string name="settings_category_groups_export">Export Speichereinstellungen für Playlists</string>
|
||||
<string name="settings_export_path_alba">Export Pfad für Alben</string>
|
||||
<string name="settings_export_path_groups">Export Pfad für Playlists</string>
|
||||
<string name="dialog_superuser_access_denied_title">Root-Zugriff verweigert</string>
|
||||
<string name="dialog_superuser_access_denied">Der Play Music Exporter benötigt Administrator Rechte, ohne diese Rechte kann die App nicht genutzt werden.</string>
|
||||
<string name="settings_auto_export_different_structure_dialog_title">Verzeichnisbaum</string>
|
||||
<string name="settings_export_overwrite_existing">Bestehende Dateien überschreiben</string>
|
||||
<string name="file_handling_category">Dateiverarbeitung</string>
|
||||
<string name="overwrite_summary">Überschreibe die exportierte Datei immer, auch wenn sie bereits existiert</string>
|
||||
<string name="overwrite_title">Bestehende Dateien überschreiben</string>
|
||||
<string name="action_refesh">Datenbank neu laden</string>
|
||||
<string name="database_reloaded">Datenbank neu geladen</string>
|
||||
<string name="debug_test_crash_handler">Teste Fehlerbehandlung</string>
|
||||
<string name="settings_donation_summery">An den Entwickler über Bitcoin spenden. Dies erfordert ein Bitcoin Wallet auf deinem Handy. Besuche die Homepage für Spenden von einem Computer.</string>
|
||||
<string name="settings_donation_title">Unterstütze mich</string>
|
||||
<string name="intro_welcome_title">Willkommen!</string>
|
||||
<string name="intro_welcome_description">Das ist der Play Music Exporter. Er ermöglicht dir Export und die Speicherung von Liedern aus Play Music als MP3 Dateien.</string>
|
||||
<string name="intro_warning_title">Warnung!</string>
|
||||
<string name="intro_warning_description">Du bist verantwortlich für das, was du mit dieser App machst. Die App könnte in deinem Land möglicherweiße illegal sein. Wir raten von Musikpiraterie und dem Stehlen anderen geistigen Eigentums ab. Das Teilen von Musik, die du mit diesem Tool exportiert hast, wäre keine gute Idee, da Google möglicherweise ein unsichtbares Wasserzeichen auf die Musik setzt, um das Google Konto zurück verfolgen zu können.</string>
|
||||
<string name="intro_storage_title">Wir benötigen Zugriff auf deinen Speicher.</string>
|
||||
<string name="intro_storage_description">Wir benötigen Zugriff auf den externen Speicher, um die Play Music Datenbank in einen Ordner zu kopieren, in dem wir das Recht haben, mit ihr zu arbeiten. Außerdem benötigen wir den Zugriff auch, um die verschlüsselten MP3s ohne ID3-Tags zu entschlüsseln und mit ID3-Tags im Exportpfad zu speichern.</string>
|
||||
<string name="intro_superuser_description">Einige der Dateien, auf die wir zugreifen müssen, befinden sich in den privaten Ordnern von Play Music. Android verhindert, dass andere Apps auf diese zugreifen können. Glücklicherweise kannst du diesen Schutz mit Root-Zugriff umgehen. Ohne Root-Zugriff können wir leider nichts machen.</string>
|
||||
<string name="intro_superuser_title">Wir benötigen Root-Zugriff.</string>
|
||||
<string name="intro_error_description">Keine Software ist perfekt. Wir haben eine automatisierte Fehlerberichterstattung hinzugefügt, um die App zu verbessern. Aktiviere sie, wenn du gerne dazu beitragen möchtest, die App zu verbessern. Falls du dies nicht aktivierst, und etwas nicht funktioniert, bist du auf eigene Faust angewiesen, da wir dir ohne Logs nicht helfen können. Die gesammelten Daten enthalten nichts, was zur Identifizierung deiner Identität verwendet werden könnte. (Auf der nächsten Seite wirst du aufgefordert werden, dies zu akzeptieren oder zu verbieten)</string>
|
||||
<string name="intro_error_title">Anonyme Fehlerbericht Erstattung.</string>
|
||||
<string name="intro_finish_description">Hinweis: Solltest du eine dieser Berechtigungen deaktivieren, wird das Tutorial erneut angezeigt.</string>
|
||||
<string name="intro_finish_title">Einführung abgeschlossen!</string>
|
||||
<string name="error_alert_dialog_title">Anonyme Fehlerbericht Erstattung?</string>
|
||||
<string name="error_alert_dialog_message">Aktiviere bitte die automatische Fehlerberichterstattung, um die App zu verbessern. Du wirst keine Hilfe bekommen, falls du dies deaktivierst, da wir keine Fehler ohne die dazugehörigen Logs beheben können. Die gesammelten Daten enthalten nichts, was zur Identifizierung deiner Identität verwendet werden könnte.</string>
|
||||
<string name="no">Nein</string>
|
||||
<string name="yes">Ja</string>
|
||||
<string name="whatever">Mir egal, lass mich einfach die App nutzen!</string>
|
||||
<string name="warning_alert_dialog_title">Verstanden?</string>
|
||||
<string name="warning_alert_dialog_message">Hast du das gelesen und verstanden?</string>
|
||||
<string name="settings_open_homepage_title">Webseite</string>
|
||||
</resources>
|
|
@ -6,7 +6,6 @@
|
|||
<item>Every Day</item>
|
||||
<item>Every 2 Days</item>
|
||||
<item>Every Week</item>
|
||||
<item>Never</item>
|
||||
</string-array>
|
||||
<string-array name="settings_export_frequency_values" translatable="false">
|
||||
<item>2160000</item>
|
||||
|
@ -14,7 +13,6 @@
|
|||
<item>86400000</item>
|
||||
<item>172800000</item>
|
||||
<item>604800000</item>
|
||||
<item>-1</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="settings_export_id3_artwork_size_value_names" translatable="false">
|
||||
|
|
|
@ -44,4 +44,6 @@
|
|||
<color name="button_navigation_drawer_active">#11000000</color>
|
||||
<color name="button_navigation_drawer_text">#88000000</color>
|
||||
<color name="button_navigation_drawer_text_active">#ef6c00</color>
|
||||
<color name="white">#ffffffff</color>
|
||||
<color name="black">#ff000000</color>
|
||||
</resources>
|
5
playmusicexporter/src/main/res/values/constants.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="countly_url" translatable="false">https://countly.jcg.re/</string>
|
||||
<string name="countly_token" translatable="false">82a1d9405388c4dd73dc9835f84c59cf4274086d</string>
|
||||
</resources>
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Play Music Exporter (beta)</string>
|
||||
<string name="app_name" translatable="false">Play Music Exporter</string>
|
||||
<string name="title_track_detail">Track Detail</string>
|
||||
|
||||
<string name="action_select_all">Select all</string>
|
||||
|
@ -50,7 +50,11 @@
|
|||
<string name="notification_export_finished_single_summery">%1$s was exported successfully!</string>
|
||||
|
||||
<string name="dialog_storage_access_denied_title">Storage access denied</string>
|
||||
<string name="dialog_storage_access_denied">The PlayMusicExporter always needs to write to the external storage. This is part of the Marshmallow fix.\n\nPlease restart the app and grant the permission.</string>
|
||||
<string name="dialog_storage_access_denied">The Play Music Exporter does not work without write access to the external storage. You can not use this app without granting it write access.</string>
|
||||
|
||||
<string name="dialog_superuser_access_denied_title">Root access denied</string>
|
||||
<string name="dialog_superuser_access_denied">The Play Music Exporter does not work without superuser access. You can not use this app without granting it superuser access.</string>
|
||||
|
||||
|
||||
<string name="toast_error_track_not_offline">You can only export offline tracks. Try to download the album offline in Play Music.</string>
|
||||
|
||||
|
@ -65,21 +69,41 @@
|
|||
<string name="settings_category_about_me">About me</string>
|
||||
<string name="settings_category_thanks">Thanks to</string>
|
||||
<string name="settings_category_develop">About this version</string>
|
||||
<string name="settings_category_auto_export_conditions">Export conditions</string>
|
||||
<string name="settings_category_auto_export_path_subdir">Export path and subdirectory structure</string>
|
||||
<string name="settings_category_debug">Debug</string>
|
||||
<string name="settings_category_alba_export">Export location for albums</string>
|
||||
<string name="settings_category_groups_export">Export location for playlists</string>
|
||||
|
||||
|
||||
<string name="settings_version_number">Version Number</string>
|
||||
<string name="settings_build_date">Build date</string>
|
||||
|
||||
<string name="settings_auto_export_enable_checkbox">Enable automatic export</string>
|
||||
<string name="settings_auto_export_require_unmetered_checkbox">Require unmetered network</string>
|
||||
<string name="settings_auto_export_require_charging_checkbox">Require charging</string>
|
||||
<string name="settings_auto_export_interval_list">Export interval</string>
|
||||
<string name="settings_auto_export_different_path_switch">Use a different export path</string>
|
||||
<string name="settings_auto_export_different_path_switch_summary">Off means that the automatic export uses the same export path as manual album exports.</string>
|
||||
<string name="settings_auto_export_different_path">Export path</string>
|
||||
<string name="settings_auto_export_different_structure_switch">Use a different subdirectory structure</string>
|
||||
<string name="settings_auto_export_different_structure_switch_summary">Off means that the automatic export uses the same subdirectory structure as albums.</string>
|
||||
<string name="settings_auto_export_different_structure">Subdirectory structure</string>
|
||||
<string name="settings_auto_export_different_structure_dialog_title">Subdirectory structure</string>
|
||||
|
||||
<string name="settings_category_export_location">Export Location</string>
|
||||
<string name="settings_export_path">Export Base Path</string>
|
||||
<string name="settings_export_subdirectory_structure_dialog_message">Use / for a new folder \n Available Tags: \n - {album-artist} \n - {album} \n - {artist} \n - {title} \n - {disc=CD $} \n - {no=$$.} \n - {year} \n - {genre} \n</string>
|
||||
<string name="settings_playlist_export_subdirectory_structure_dialog_message">Use / for a new folder \n Available Tags: \n - {playlist} \n - {album-artist} \n - {album} \n - {artist} \n - {title} \n - {disc=CD $} \n - {no=$$.} \n - {year} \n - {genre} \n</string>
|
||||
|
||||
<string name="settings_export_path_alba">Export path for albums</string>
|
||||
<string name="settings_export_structure_alba">Subdirectory structure for albums</string>
|
||||
<string name="settings_export_subdirectory_structure_album">Set your subdirectory structure</string>
|
||||
<string name="settings_export_structure_alba_dialog_title">Set your subdirectory structure</string>
|
||||
<string name="settings_export_subdirectory_structure_album_example">Example: Beatles/Help!/13. Yesterday.mp3</string>
|
||||
|
||||
<string name="settings_export_path_groups">Export path for playlists</string>
|
||||
<string name="settings_export_structure_groups">Subdirectory structure for playlists</string>
|
||||
<string name="settings_export_subdirectory_structure_group">Set your subdirectory structure</string>
|
||||
<string name="settings_export_structure_groups_dialog_title">Set your subdirectory structure</string>
|
||||
<string name="settings_export_subdirectory_structure_group_example">Example: Great Songs/4. Beatles - Yesterday.mp3</string>
|
||||
|
||||
<string name="settings_export_id3">ID3 Tags</string>
|
||||
|
@ -92,12 +116,13 @@
|
|||
<string name="settings_export_id3_artwork_size_summary">If the artwork original size is larger than this setting the app will size down the artwork to this size.</string>
|
||||
|
||||
<string name="settings_export_path_custom">Custom path</string>
|
||||
<string name="settings_export_overwrite_existing">Overwrite existing files</string>
|
||||
|
||||
<string name="settings_open_old_homepage_title" translatable="false">David-Schulte.de</string>
|
||||
<string name="settings_open_old_homepage_url" translatable="false"><![CDATA[http://www.david-schulte.de/]]></string>
|
||||
|
||||
<string name="settings_open_homepage_title" translatable="false">jcg.re</string>
|
||||
<string name="settings_open_homepage_url" translatable="false"><![CDATA[https://jcg.re/]]></string>
|
||||
<string name="settings_open_homepage_title">Website</string>
|
||||
<string name="settings_open_homepage_url" translatable="false"><![CDATA[https://github.com/jcgruenhage/PlayMusicExporter]]></string>
|
||||
|
||||
<string name="settings_mp3agic_title" translatable="false">Mp3agic ID3 Libary</string>
|
||||
<string name="settings_mp3agic_summery" translatable="false">Michael Patricios © 2006–2013</string>
|
||||
|
@ -108,10 +133,42 @@
|
|||
<string name="settings_donation_old_url" translatable="false"><![CDATA[https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A2VD24Z5E32RU]]></string>
|
||||
|
||||
<string name="action_mode_track_selection">%d tracks selected</string>
|
||||
|
||||
<string name="pref_header_export">Export</string>
|
||||
<string name="pref_header_automation">Automation</string>
|
||||
<string name="pref_header_about">About</string>
|
||||
<string name="pref_header_debug">Debug</string>
|
||||
|
||||
<string name="settings_export_structure_alba_default_value" translatable="false">{album-artist}/{album}/{disc=CD $}/{no=$$.} {title}.mp3</string>
|
||||
<string name="settings_export_structure_groups_default_value" translatable="false">{group}/{group-no=$$.} {artist} - {title}.mp3</string>
|
||||
<string name="settings_export_structure_groups_default_value" translatable="false">{playlist}/{artist} - {title}.mp3</string>
|
||||
|
||||
<string name="debug_trigger_export_all_title">Trigger ExportAllService now</string>
|
||||
<string name="file_handling_category">File Handling</string>
|
||||
<string name="overwrite_summary">Always overwrite the exported file, even if it already exists</string>
|
||||
<string name="overwrite_title">Overwrite Existing Files</string>
|
||||
<string name="action_refesh">Refresh</string>
|
||||
<string name="database_reloaded">Music Database Reloaded</string>
|
||||
<string name="debug_test_crash_handler">Test Crash Handler</string>
|
||||
<string name="settings_donation_title">Support me</string>
|
||||
<string name="settings_donation_summery">Donate to the current developer via Bitcoin. This requires that you have installed a bitcoin wallet on your phone. For donations from a computer, see the homepage.</string>
|
||||
<string name="settings_donation_url" translatable="false"><![CDATA[bitcoin:1NdzpDWPQ53xWT5fraGPZX5F9XrKiPBXjp]]></string>
|
||||
<string name="intro_welcome_title">Welcome!</string>
|
||||
<string name="intro_welcome_description">This is the Play Music Exporter. It can export songs from Play Music and save them as MP3 files where you want them to be.</string>
|
||||
<string name="intro_warning_title">Warning!</string>
|
||||
<string name="intro_warning_description">You are responsible for what you do with this app. Depending on where you live it might be illegal to use this app. We discourage piracy of music and other intellectual property. Sharing music you exported with this tool might be a very bad idea, Google could put an invisible watermark on the music, so that people can trace the MP3s back to the owner of the Google account that was used.</string>
|
||||
<string name="intro_storage_title">We need access to your storage.</string>
|
||||
<string name="intro_storage_description">We need to access the external storage, for copying the Play Music database to a folder, where we have the right to work with it. We also need access to the external storage, to finish up the MP3s, from encrypted without ID3 tags, to decrypted with ID3 tags, before we save them to your export path.</string>
|
||||
<string name="intro_superuser_description">Some of the files we need to access are in the private folders of Play Music. Android prevents apps from accessing the private folders of other apps, but luckily, you can circumvent this protection with root access. Without root access this app can\'t do anything.</string>
|
||||
<string name="intro_superuser_title">We need root access.</string>
|
||||
<string name="intro_error_description">No single piece of software is perfect. To get this closer though, we have added automated error reporting to this app. If you would like to help make this app better, let us enable that. If you don\'t enable this and something does not work, you are on your own, since we can\'t help you without having the logs this provides us with. The data collected by this does not include anything that could be used to identify you. (You will be prompted to either accept or deny that when you go to the next slide)</string>
|
||||
<string name="intro_error_title">Anonymous error reporting.</string>
|
||||
<string name="intro_finish_description">One note: Should you revoke any of these permission, the tutorial will be shown again on the next launch.</string>
|
||||
<string name="intro_finish_title">Introduction finished!</string>
|
||||
<string name="error_alert_dialog_title">Anonymous error reporting?</string>
|
||||
<string name="error_alert_dialog_message">In order to make this app better, please enable automatic crash reporting. You wont ever get any help, if you disable this, since we can\'t fix bugs without the logs provided by this. The data collected by this does not include anything that could be used to identify you.</string>
|
||||
<string name="no">No</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="whatever">I don\'t care, just let me use the app!</string>
|
||||
<string name="warning_alert_dialog_title">Understood?</string>
|
||||
<string name="warning_alert_dialog_message">Have you read and understood this?</string>
|
||||
</resources>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</Preference>
|
||||
</PreferenceCategory>
|
||||
|
||||
<!-- About David Schulte -->
|
||||
<!-- About this project -->
|
||||
<PreferenceCategory android:title="@string/settings_category_about_me">
|
||||
<!-- Homepage -->
|
||||
<Preference
|
||||
|
@ -22,6 +22,15 @@
|
|||
android:action="android.intent.action.VIEW"
|
||||
android:data="@string/settings_open_homepage_url" />
|
||||
</Preference>
|
||||
|
||||
<!-- Donation -->
|
||||
<Preference
|
||||
android:summary="@string/settings_donation_summery"
|
||||
android:title="@string/settings_donation_title">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:data="@string/settings_donation_url" />
|
||||
</Preference>
|
||||
</PreferenceCategory>
|
||||
|
||||
<!-- About David Schulte -->
|
||||
|
@ -47,13 +56,14 @@
|
|||
|
||||
<!-- Develop -->
|
||||
<PreferenceCategory android:title="@string/settings_category_develop">
|
||||
<!-- TODO change Version number and build date with releases -->
|
||||
<!-- Version number-->
|
||||
<Preference
|
||||
android:summary="3.0.0"
|
||||
android:key="about_version_number"
|
||||
android:title="@string/settings_version_number" />
|
||||
<!-- Build date-->
|
||||
<Preference
|
||||
android:summary="29.11.2016"
|
||||
android:key="about_build_date"
|
||||
android:title="@string/settings_build_date" />
|
||||
</PreferenceCategory>
|
||||
|
||||
|
|
|
@ -1,23 +1,40 @@
|
|||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<Preference
|
||||
android:title="System Requirements"
|
||||
android:summary="Automated export requires Android 5.0 Lollipop, so enabling this on lower Android versions won't do anything."/>
|
||||
<PreferenceCategory android:title="Automation Settings">
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="preference_auto_export_enabled"
|
||||
android:title="@string/settings_auto_export_enable_checkbox" />
|
||||
<ListPreference
|
||||
android:entries="@array/settings_export_frequency_value_names"
|
||||
android:entryValues="@array/settings_export_frequency_values"
|
||||
android:key="preference_auto_export_frequency"
|
||||
android:title="@string/settings_auto_export_interval_list" />
|
||||
<PreferenceCategory android:title="@string/settings_category_auto_export_path_subdir">
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="preference_automatic_export_enabled"
|
||||
android:title="Enable Automatic Export" />
|
||||
<ListPreference
|
||||
android:entries="@array/settings_export_frequency_value_names"
|
||||
android:entryValues="@array/settings_export_frequency_values"
|
||||
android:key="preference_automatic_export_frequency"
|
||||
android:title="Export Interval" />
|
||||
android:key="preference_auto_export_use_different_path"
|
||||
android:summary="@string/settings_auto_export_different_path_switch_summary"
|
||||
android:title="@string/settings_auto_export_different_path_switch" />
|
||||
<Preference
|
||||
android:key="preference_auto_export_path"
|
||||
android:title="@string/settings_auto_export_different_path" />
|
||||
<SwitchPreference
|
||||
android:key="preference_auto_export_use_different_structure"
|
||||
android:summary="@string/settings_auto_export_different_structure_switch_summary"
|
||||
android:title="@string/settings_auto_export_different_structure_switch" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="@string/settings_export_structure_alba_default_value"
|
||||
android:dialogMessage="@string/settings_export_subdirectory_structure_dialog_message"
|
||||
android:dialogTitle="@string/settings_auto_export_different_structure_dialog_title"
|
||||
android:hint="@string/settings_export_structure_alba_default_value"
|
||||
android:key="preference_auto_export_structure"
|
||||
android:summary="@string/settings_export_subdirectory_structure_album_example"
|
||||
android:title="@string/settings_auto_export_different_structure" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/settings_category_auto_export_conditions">
|
||||
<CheckBoxPreference
|
||||
android:key="preference_automatic_export_require_charging"
|
||||
android:title="Require Charging?" />
|
||||
android:key="preference_auto_export_require_charging"
|
||||
android:title="@string/settings_auto_export_require_charging_checkbox" />
|
||||
<CheckBoxPreference
|
||||
android:key="preference_automatic_export_require_unmetered"
|
||||
android:title="Require Unmetered Network?" />
|
||||
|
||||
android:key="preference_auto_export_require_unmetered"
|
||||
android:title="@string/settings_auto_export_require_unmetered_checkbox" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
|
|
@ -2,5 +2,7 @@
|
|||
<PreferenceCategory android:title="@string/settings_category_debug">
|
||||
<Preference android:title="@string/debug_trigger_export_all_title"
|
||||
android:key="debug_trigger_export_all"/>
|
||||
<Preference android:title="@string/debug_test_crash_handler"
|
||||
android:key="debug_test_crash_handler"/>
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
|
7
playmusicexporter/src/main/res/xml/pref_debug_header.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<header
|
||||
android:fragment="re.jcg.playmusicexporter.activities.SettingsActivity$DebugPreferenceFragment"
|
||||
android:icon="@drawable/ic_action_settings"
|
||||
android:title="@string/pref_header_debug" />
|
||||
|
||||
</preference-headers>
|
|
@ -1,28 +1,33 @@
|
|||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_category_export_location">
|
||||
<PreferenceCategory android:title="@string/settings_category_alba_export">
|
||||
<!-- Base path for alba -->
|
||||
<Preference
|
||||
android:key="preference_export_path"
|
||||
android:title="@string/settings_export_path">
|
||||
</Preference>
|
||||
android:key="preference_alba_export_path"
|
||||
android:title="@string/settings_export_path_alba" />
|
||||
|
||||
<!-- Path structure for albums -->
|
||||
<!-- Path structure for alba -->
|
||||
<EditTextPreference
|
||||
android:defaultValue="@string/settings_export_structure_alba_default_value"
|
||||
android:dialogMessage="@string/settings_export_subdirectory_structure_dialog_message"
|
||||
android:dialogTitle="@string/settings_export_subdirectory_structure_album"
|
||||
android:dialogTitle="@string/settings_export_structure_alba_dialog_title"
|
||||
android:hint="@string/settings_export_structure_alba_default_value"
|
||||
android:key="preference_structure_alba"
|
||||
android:key="preference_alba_export_structure"
|
||||
android:summary="@string/settings_export_subdirectory_structure_album_example"
|
||||
android:title="@string/settings_export_structure_alba" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/settings_category_groups_export">
|
||||
<!-- Base path for groups -->
|
||||
<Preference
|
||||
android:key="preference_groups_export_path"
|
||||
android:title="@string/settings_export_path_groups" />
|
||||
|
||||
<!-- Path structure for groups -->
|
||||
<EditTextPreference
|
||||
android:defaultValue="@string/settings_export_structure_groups_default_value"
|
||||
android:dialogMessage="@string/settings_export_subdirectory_structure_dialog_message"
|
||||
android:dialogTitle="@string/settings_export_subdirectory_structure_group"
|
||||
android:dialogMessage="@string/settings_playlist_export_subdirectory_structure_dialog_message"
|
||||
android:dialogTitle="@string/settings_export_structure_groups_dialog_title"
|
||||
android:hint="@string/settings_export_structure_groups_default_value"
|
||||
android:key="preference_structure_groups"
|
||||
android:key="preference_groups_export_structure"
|
||||
android:summary="@string/settings_export_subdirectory_structure_group_example"
|
||||
android:title="@string/settings_export_structure_groups" />
|
||||
</PreferenceCategory>
|
||||
|
@ -44,4 +49,10 @@
|
|||
android:summary="@string/settings_export_id3_artwork_size_summary"
|
||||
android:title="@string/settings_export_id3_artwork_size" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/file_handling_category">
|
||||
<SwitchPreference
|
||||
android:key="preference_overwrite_existing_files"
|
||||
android:summary="@string/overwrite_summary"
|
||||
android:title="@string/overwrite_title" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
|
|
@ -16,9 +16,5 @@
|
|||
android:fragment="re.jcg.playmusicexporter.activities.SettingsActivity$AboutPreferenceFragment"
|
||||
android:icon="@drawable/ic_info_black_24dp"
|
||||
android:title="@string/pref_header_about" />
|
||||
<header
|
||||
android:fragment="re.jcg.playmusicexporter.activities.SettingsActivity$DebugPreferenceFragment"
|
||||
android:icon="@drawable/ic_action_settings"
|
||||
android:title="Debug" />
|
||||
|
||||
</preference-headers>
|
||||
|
|
|
@ -27,7 +27,7 @@ android {
|
|||
buildToolsVersion "25.0.2"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
minSdkVersion 11
|
||||
targetSdkVersion 25
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
@ -41,6 +41,6 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile project(':framework')
|
||||
compile 'com.mpatric:mp3agic:0.9.0'
|
||||
}
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":playmusiclib" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="PlayMusicExporter" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":playmusiclib" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<afterSyncTasks>
|
||||
<task>generateDebugSources</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" />
|
||||
<option name="LIBRARY_PROJECT" value="true" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
|
||||
<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/jni" 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/jni" 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/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/jni" 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/jni" 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/jni" 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/annotations" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/typedefs.txt" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="mp3agic-0.8.4" level="project" />
|
||||
<orderEntry type="module" module-name="framework" exported="" />
|
||||
<orderEntry type="library" exported="" name="support-compat-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-fragment-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="animated-vector-drawable-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-core-ui-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-media-compat-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-vector-drawable-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-25.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-core-utils-25.1.0" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -69,8 +69,13 @@ public class ArtworkLoader {
|
|||
if (!TextUtils.isEmpty(artworkUrl)) {
|
||||
// Tries to load the artwork via internet
|
||||
try {
|
||||
URL url = new URL(artworkUrl);
|
||||
bitmap = BitmapFactory.decodeStream(url.openStream());
|
||||
if (artworkUrl.contains("mediastore")) {
|
||||
Logger.getInstance().logError("LoadArtwork", "mediastore artwork");
|
||||
//TODO handle mediastore album art using ContentResolver
|
||||
} else {
|
||||
URL url = new URL(artworkUrl);
|
||||
bitmap = BitmapFactory.decodeStream(url.openStream());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Error
|
||||
Logger.getInstance().logError("LoadArtwork", e.toString());
|
||||
|
|
|
@ -41,7 +41,10 @@ import com.mpatric.mp3agic.ID3v2;
|
|||
import com.mpatric.mp3agic.ID3v22Tag;
|
||||
import com.mpatric.mp3agic.ID3v23Tag;
|
||||
import com.mpatric.mp3agic.ID3v24Tag;
|
||||
import com.mpatric.mp3agic.InvalidDataException;
|
||||
import com.mpatric.mp3agic.Mp3File;
|
||||
import com.mpatric.mp3agic.NotSupportedException;
|
||||
import com.mpatric.mp3agic.UnsupportedTagException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
|
@ -51,6 +54,7 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import de.arcus.framework.logger.Logger;
|
||||
import de.arcus.framework.superuser.SuperUser;
|
||||
|
@ -134,10 +138,11 @@ public class PlayMusicManager {
|
|||
|
||||
/**
|
||||
* The database will be copied to a temp folder to access from the app
|
||||
*
|
||||
* @return Gets the temp path to the database
|
||||
*/
|
||||
private String getTempDatabasePath() {
|
||||
return getTempPath() + "/music.db";
|
||||
return getTempPath() + "music.db";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -293,6 +298,7 @@ public class PlayMusicManager {
|
|||
|
||||
/**
|
||||
* Creates a new PlayMusic manager
|
||||
*
|
||||
* @param context App context
|
||||
*/
|
||||
public PlayMusicManager(Context context) {
|
||||
|
@ -303,8 +309,9 @@ public class PlayMusicManager {
|
|||
|
||||
/**
|
||||
* Loads all needed information and opens the database
|
||||
* @throws PlayMusicNotFoundException PlayMusic is not installed
|
||||
* @throws NoSuperUserException No super user permissions
|
||||
*
|
||||
* @throws PlayMusicNotFoundException PlayMusic is not installed
|
||||
* @throws NoSuperUserException No super user permissions
|
||||
* @throws CouldNotOpenDatabaseException Could not open the database
|
||||
*/
|
||||
public void startUp() throws PlayMusicNotFoundException, NoSuperUserException, CouldNotOpenDatabaseException {
|
||||
|
@ -341,7 +348,8 @@ public class PlayMusicManager {
|
|||
|
||||
/**
|
||||
* Copies the database to a temp directory and opens it
|
||||
* @throws NoSuperUserException No super user permissions
|
||||
*
|
||||
* @throws NoSuperUserException No super user permissions
|
||||
* @throws de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException Could not open the database
|
||||
*/
|
||||
private void loadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
|
||||
|
@ -366,10 +374,11 @@ public class PlayMusicManager {
|
|||
|
||||
/**
|
||||
* Reloads the database from PlayMusic
|
||||
* @throws NoSuperUserException No super user permissions
|
||||
*
|
||||
* @throws NoSuperUserException No super user permissions
|
||||
* @throws CouldNotOpenDatabaseException Could not open the database
|
||||
*/
|
||||
public void realoadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
|
||||
public void reloadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
|
||||
// Reload database
|
||||
loadDatabase();
|
||||
}
|
||||
|
@ -399,6 +408,7 @@ public class PlayMusicManager {
|
|||
|
||||
/**
|
||||
* Gets the path to the music track
|
||||
*
|
||||
* @param localCopyPath The local copy path
|
||||
* @return The path to the music file
|
||||
*/
|
||||
|
@ -433,6 +443,7 @@ public class PlayMusicManager {
|
|||
|
||||
/**
|
||||
* Gets the full path to the artwork
|
||||
*
|
||||
* @param artworkPath The artwork path
|
||||
* @return The full path to the artwork
|
||||
*/
|
||||
|
@ -443,8 +454,8 @@ public class PlayMusicManager {
|
|||
String path;
|
||||
|
||||
// Fix the path for Play Music 5.9.1854
|
||||
if (!artworkPath.startsWith("artwork2/folder/"))
|
||||
artworkPath = "artwork2/folder/" + artworkPath;
|
||||
if (!artworkPath.startsWith("artwork/"))
|
||||
artworkPath = "artwork/" + artworkPath;
|
||||
|
||||
// Search in the public data
|
||||
for (String publicData : mPathPublicData) {
|
||||
|
@ -465,11 +476,13 @@ public class PlayMusicManager {
|
|||
|
||||
/**
|
||||
* Exports a track to the sd card
|
||||
* @param musicTrack The music track you want to export
|
||||
* @param dest The destination path
|
||||
*
|
||||
* @param musicTrack The music track you want to export
|
||||
* @param dest The destination path
|
||||
* @param forceOverwrite Forces overwrite of the destination file
|
||||
* @return Returns whether the export was successful
|
||||
*/
|
||||
public boolean exportMusicTrack(MusicTrack musicTrack, String dest) {
|
||||
public boolean exportMusicTrack(MusicTrack musicTrack, String dest, boolean forceOverwrite) throws InvalidDataException, IOException, UnsupportedTagException, NotSupportedException {
|
||||
// Creates the destination directory
|
||||
File directory = new File(dest).getParentFile();
|
||||
|
||||
|
@ -477,16 +490,18 @@ public class PlayMusicManager {
|
|||
// Filename
|
||||
String filename = new File(dest).getName();
|
||||
|
||||
return exportMusicTrack(musicTrack, Uri.fromFile(directory), filename);
|
||||
return exportMusicTrack(musicTrack, Uri.fromFile(directory), filename, forceOverwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports a track to the sd card
|
||||
* @param musicTrack The music track you want to export
|
||||
* @param uri The document tree
|
||||
*
|
||||
* @param musicTrack The music track you want to export
|
||||
* @param uri The document tree
|
||||
* @param forceOverwrite Forces overwrite of the destination file
|
||||
* @return Returns whether the export was successful
|
||||
*/
|
||||
public boolean exportMusicTrack(MusicTrack musicTrack, Uri uri, String path) {
|
||||
public boolean exportMusicTrack(MusicTrack musicTrack, Uri uri, String path, boolean forceOverwrite) throws InvalidDataException, IOException, UnsupportedTagException, NotSupportedException {
|
||||
|
||||
// Check for null
|
||||
if (musicTrack == null) return false;
|
||||
|
@ -496,94 +511,111 @@ public class PlayMusicManager {
|
|||
// Could not find the source file
|
||||
if (srcFile == null) return false;
|
||||
|
||||
String fileTmp = getTempPath() + "/tmp.mp3";
|
||||
String uniqueID = UUID.randomUUID().toString();
|
||||
|
||||
// Copy to temp path failed
|
||||
if (!SuperUserTools.fileCopy(srcFile, fileTmp))
|
||||
return false;
|
||||
if (forceOverwrite || !isAlreadyThere(uri, path)) {
|
||||
|
||||
// Encrypt the file
|
||||
if (musicTrack.isEncoded()) {
|
||||
String fileTmpCrypt = getTempPath() + "/crypt.mp3";
|
||||
String fileTmp = getTempPath() + uniqueID + "_tmp.mp3";
|
||||
|
||||
// Encrypts the file
|
||||
if (trackEncrypt(musicTrack, fileTmp, fileTmpCrypt)) {
|
||||
// Remove the old tmp file
|
||||
FileTools.fileDelete(fileTmp);
|
||||
// Copy to temp path failed
|
||||
if (!SuperUserTools.fileCopy(srcFile, fileTmp))
|
||||
return false;
|
||||
|
||||
// New tmp file
|
||||
fileTmp = fileTmpCrypt;
|
||||
} else {
|
||||
Logger.getInstance().logWarning("ExportMusicTrack", "Encrypting failed! Continue with decrypted file.");
|
||||
// Encrypt the file
|
||||
if (musicTrack.isEncoded()) {
|
||||
String fileTmpCrypt = getTempPath() + uniqueID + "_crypt.mp3";
|
||||
|
||||
// Encrypts the file
|
||||
if (trackEncrypt(musicTrack, fileTmp, fileTmpCrypt)) {
|
||||
// Remove the old tmp file
|
||||
FileTools.fileDelete(fileTmp);
|
||||
|
||||
// New tmp file
|
||||
fileTmp = fileTmpCrypt;
|
||||
} else {
|
||||
Logger.getInstance().logWarning("ExportMusicTrack", "Encrypting failed! Continue with decrypted file.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String dest;
|
||||
Uri copyUri = null;
|
||||
if (uri.toString().startsWith("file://")) {
|
||||
// Build the full path
|
||||
dest = uri.buildUpon().appendPath(path).build().getPath();
|
||||
|
||||
String dest;
|
||||
Uri copyUri = null;
|
||||
if (uri.toString().startsWith("file://")) {
|
||||
// Build the full path
|
||||
dest = uri.buildUpon().appendPath(path).build().getPath();
|
||||
String parentDirectory = new File(dest).getParent();
|
||||
FileTools.directoryCreate(parentDirectory);
|
||||
} else {
|
||||
// Complex uri (Lollipop)
|
||||
dest = getTempPath() + uniqueID + "_final.mp3";
|
||||
|
||||
String parentDirectory = new File(dest).getParent();
|
||||
FileTools.directoryCreate(parentDirectory);
|
||||
} else {
|
||||
// Complex uri (Lollipop)
|
||||
dest = getTempPath() + "/final.mp3";
|
||||
// The root
|
||||
DocumentFile document = DocumentFile.fromTreeUri(mContext, uri);
|
||||
|
||||
// The root
|
||||
DocumentFile document = DocumentFile.fromTreeUri(mContext, uri);
|
||||
// Creates the subdirectories
|
||||
String[] directories = path.split("/");
|
||||
for (int i = 0; i < directories.length - 1; i++) {
|
||||
String directoryName = directories[i];
|
||||
boolean found = false;
|
||||
|
||||
// Creates the subdirectories
|
||||
String[] directories = path.split("\\/");
|
||||
for(int i=0; i<directories.length - 1; i++) {
|
||||
String directoryName = directories[i];
|
||||
boolean found = false;
|
||||
// Search all sub elements
|
||||
for (DocumentFile subDocument : document.listFiles()) {
|
||||
// Directory exists
|
||||
if (subDocument.isDirectory() && subDocument.getName().equalsIgnoreCase(directoryName)) {
|
||||
document = subDocument;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Search all sub elements
|
||||
for (DocumentFile subDocument: document.listFiles()) {
|
||||
// Directory exists
|
||||
if (subDocument.isDirectory() && subDocument.getName().equals(directoryName)) {
|
||||
document = subDocument;
|
||||
found = true;
|
||||
break;
|
||||
if (!found) {
|
||||
// Create the directory
|
||||
document = document.createDirectory(directoryName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// Create the directory
|
||||
document = document.createDirectory(directoryName);
|
||||
// Gets the filename
|
||||
String filename = directories[directories.length - 1];
|
||||
|
||||
for (DocumentFile subDocument : document.listFiles()) {
|
||||
// Directory exists
|
||||
if (subDocument.isFile()) {
|
||||
if (filename != null && subDocument.getName().equalsIgnoreCase(filename)) {
|
||||
// Delete the file
|
||||
if (forceOverwrite) {
|
||||
Logger.getInstance().logWarning("ExportMusicTrack", "(forceOverwrite) Deleting original file: " + filename);
|
||||
}
|
||||
subDocument.delete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the mp3 file
|
||||
document = document.createFile("music/mp3", filename);
|
||||
|
||||
// Create the directories
|
||||
copyUri = document.getUri();
|
||||
}
|
||||
|
||||
// Gets the filename
|
||||
String filename = directories[directories.length - 1];
|
||||
|
||||
for (DocumentFile subDocument: document.listFiles()) {
|
||||
// Directory exists
|
||||
if (subDocument.isFile() && subDocument.getName().equals(filename)) {
|
||||
// Delete the file
|
||||
subDocument.delete();
|
||||
break;
|
||||
// We want to export the ID3 tags
|
||||
if (mID3Enable) {
|
||||
// Adds the meta data
|
||||
if (!trackWriteID3(musicTrack, fileTmp, dest)) {
|
||||
Logger.getInstance().logWarning("ExportMusicTrack", "ID3 writer failed! Continue without ID3 tags.");
|
||||
|
||||
// Failed, moving without meta data
|
||||
if (!FileTools.fileMove(fileTmp, dest)) {
|
||||
Logger.getInstance().logError("ExportMusicTrack", "Moving the raw file failed!");
|
||||
|
||||
// Could not copy the file
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the mp3 file
|
||||
document = document.createFile("music/mp3", filename);
|
||||
|
||||
// Create the directories
|
||||
copyUri = document.getUri();
|
||||
}
|
||||
|
||||
|
||||
// We want to export the ID3 tags
|
||||
if (mID3Enable) {
|
||||
// Adds the meta data
|
||||
if (!trackWriteID3(musicTrack, fileTmp, dest)) {
|
||||
Logger.getInstance().logWarning("ExportMusicTrack", "ID3 writer failed! Continue without ID3 tags.");
|
||||
|
||||
// Failed, moving without meta data
|
||||
} else {
|
||||
// Moving the file
|
||||
if (!FileTools.fileMove(fileTmp, dest)) {
|
||||
Logger.getInstance().logError("ExportMusicTrack", "Moving the raw file failed!");
|
||||
|
||||
|
@ -591,180 +623,201 @@ public class PlayMusicManager {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Moving the file
|
||||
if (!FileTools.fileMove(fileTmp, dest)) {
|
||||
Logger.getInstance().logError("ExportMusicTrack", "Moving the raw file failed!");
|
||||
|
||||
// Could not copy the file
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// We need to copy the file to a uri
|
||||
if (copyUri != null) {
|
||||
// Lollipop only
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
try {
|
||||
// Gets the file descriptor
|
||||
ParcelFileDescriptor parcelFileDescriptor = mContext.getContentResolver().openFileDescriptor(copyUri, "w");
|
||||
|
||||
// We need to copy the file to a uri
|
||||
if (copyUri != null) {
|
||||
// Lollipop only
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
try {
|
||||
// Gets the file descriptor
|
||||
ParcelFileDescriptor parcelFileDescriptor = mContext.getContentResolver().openFileDescriptor(copyUri, "w");
|
||||
// Gets the output stream
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
|
||||
|
||||
// Gets the output stream
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
|
||||
// Gets the input stream
|
||||
FileInputStream fileInputStream = new FileInputStream(dest);
|
||||
|
||||
// Gets the input stream
|
||||
FileInputStream fileInputStream = new FileInputStream(dest);
|
||||
// Copy the stream
|
||||
FileTools.fileCopy(fileInputStream, fileOutputStream);
|
||||
|
||||
// Copy the stream
|
||||
FileTools.fileCopy(fileInputStream, fileOutputStream);
|
||||
// Close all streams
|
||||
fileOutputStream.close();
|
||||
fileInputStream.close();
|
||||
parcelFileDescriptor.close();
|
||||
|
||||
// Close all streams
|
||||
fileOutputStream.close();
|
||||
fileInputStream.close();
|
||||
parcelFileDescriptor.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
Logger.getInstance().logError("ExportMusicTrack", "File not found!");
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
Logger.getInstance().logError("ExportMusicTrack", "File not found!");
|
||||
// Could not copy the file
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Logger.getInstance().logError("ExportMusicTrack", "Failed to write the document: " + e.toString());
|
||||
|
||||
// Could not copy the file
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Logger.getInstance().logError("ExportMusicTrack", "Failed to write the document: " + e.toString());
|
||||
|
||||
// Could not copy the file
|
||||
return false;
|
||||
// Could not copy the file
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete temp files
|
||||
cleanUp(uniqueID);
|
||||
|
||||
// Adds the file to the media system
|
||||
//new MediaScanner(mContext, dest);
|
||||
|
||||
} else {
|
||||
Logger.getInstance().logInfo("exportMusicTrack", path + " already exists, skipping.");
|
||||
}
|
||||
|
||||
// Delete temp files
|
||||
cleanUp();
|
||||
|
||||
// Adds the file to the media system
|
||||
//new MediaScanner(mContext, dest);
|
||||
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the music file to a new path and adds the mp3 meta data
|
||||
* @param musicTrack Track information
|
||||
* @param src The source mp3 file
|
||||
* @param dest The destination path
|
||||
* return Return if the operation was successful
|
||||
* Checks if the destination file already exists
|
||||
*
|
||||
* @param pUri the source file
|
||||
* @param pPath The destination
|
||||
* return true if the file already exists
|
||||
*/
|
||||
private boolean trackWriteID3(MusicTrack musicTrack, String src, String dest) {
|
||||
try {
|
||||
// Opens the mp3
|
||||
Mp3File mp3File = new Mp3File(src);
|
||||
|
||||
// Removes all existing tags
|
||||
mp3File.removeId3v1Tag();
|
||||
mp3File.removeId3v2Tag();
|
||||
mp3File.removeCustomTag();
|
||||
|
||||
// We want to add a fallback ID3v1 tag
|
||||
if (mID3EnableFallback) {
|
||||
// Create a new tag with ID3v1
|
||||
ID3v1Tag tagID3v1 = new ID3v1Tag();
|
||||
|
||||
// Set all tag values
|
||||
tagID3v1.setTrack(musicTrack.getTitle());
|
||||
tagID3v1.setArtist(musicTrack.getArtist());
|
||||
tagID3v1.setAlbum(musicTrack.getAlbum());
|
||||
tagID3v1.setYear(musicTrack.getYear());
|
||||
|
||||
// Search the genre
|
||||
for(int n=0; n<ID3v1Genres.GENRES.length; n++) {
|
||||
// Genre found
|
||||
if (ID3v1Genres.GENRES[n].equals(musicTrack.getGenre())) {
|
||||
tagID3v1.setGenre(n);
|
||||
break;
|
||||
private boolean isAlreadyThere(Uri pUri, String pPath) {
|
||||
if (pUri.toString().startsWith("file://")) {
|
||||
//Old sdcard URI
|
||||
return FileTools.fileExists(pUri.buildUpon().appendPath(pPath).build().toString());
|
||||
} else {
|
||||
//Documents Provider URI
|
||||
DocumentFile lDocumentFile = DocumentFile.fromTreeUri(mContext, pUri);
|
||||
for (String lDisplayName : pPath.split("/")) {
|
||||
if (lDocumentFile.findFile(lDisplayName) != null) {
|
||||
lDocumentFile = lDocumentFile.findFile(lDisplayName);
|
||||
if (lDocumentFile.length() == 0) {
|
||||
if (!lDocumentFile.isDirectory()) {
|
||||
Logger.getInstance().logInfo("isAlreadyThere", pPath + " File exists, but is 0 bytes in size.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logger.getInstance().logInfo("isAlreadyThere", pPath + " does not exist yet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
mp3File.setId3v1Tag(tagID3v1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// It can't be null
|
||||
final ID3v2 tagID3v2;
|
||||
/**
|
||||
* Copies the music file to a new path and adds the mp3 meta data
|
||||
*
|
||||
* @param musicTrack Track information
|
||||
* @param src The source mp3 file
|
||||
* @param dest The destination path
|
||||
* return Return if the operation was successful
|
||||
*/
|
||||
private boolean trackWriteID3(MusicTrack musicTrack, String src, String dest) throws InvalidDataException, IOException, UnsupportedTagException, NotSupportedException {
|
||||
// Opens the mp3
|
||||
Mp3File mp3File = new Mp3File(src);
|
||||
|
||||
// Creates the requested version
|
||||
switch(mID3v2Version) {
|
||||
case ID3v22:
|
||||
tagID3v2 = new ID3v22Tag();
|
||||
break;
|
||||
case ID3v23:
|
||||
tagID3v2 = new ID3v23Tag();
|
||||
break;
|
||||
case ID3v24:
|
||||
tagID3v2 = new ID3v24Tag();
|
||||
break;
|
||||
default:
|
||||
tagID3v2 = null;
|
||||
break;
|
||||
}
|
||||
// Removes all existing tags
|
||||
mp3File.removeId3v1Tag();
|
||||
mp3File.removeId3v2Tag();
|
||||
mp3File.removeCustomTag();
|
||||
|
||||
// We want to add a fallback ID3v1 tag
|
||||
if (mID3EnableFallback) {
|
||||
// Create a new tag with ID3v1
|
||||
ID3v1Tag tagID3v1 = new ID3v1Tag();
|
||||
|
||||
// Set all tag values
|
||||
tagID3v2.setTitle(musicTrack.getTitle());
|
||||
tagID3v2.setArtist(musicTrack.getArtist());
|
||||
tagID3v2.setAlbum(musicTrack.getAlbum());
|
||||
tagID3v2.setAlbumArtist(musicTrack.getAlbumArtist());
|
||||
tagID3v2.setTrack("" + musicTrack.getTrackNumber());
|
||||
tagID3v2.setPartOfSet("" + musicTrack.getDiscNumber());
|
||||
tagID3v2.setYear(musicTrack.getYear());
|
||||
tagID3v1.setTrack(musicTrack.getTitle());
|
||||
tagID3v1.setArtist(musicTrack.getArtist());
|
||||
tagID3v1.setAlbum(musicTrack.getAlbum());
|
||||
tagID3v1.setYear(musicTrack.getYear());
|
||||
|
||||
if (!TextUtils.isEmpty(musicTrack.getGenre())) {
|
||||
try {
|
||||
// Maybe the genre is not supported
|
||||
tagID3v2.setGenreDescription(musicTrack.getGenre());
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logger.getInstance().logWarning("TrackWriteID3", e.getMessage());
|
||||
// Search the genre
|
||||
for (int n = 0; n < ID3v1Genres.GENRES.length; n++) {
|
||||
// Genre found
|
||||
if (ID3v1Genres.GENRES[n].equals(musicTrack.getGenre())) {
|
||||
tagID3v1.setGenre(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the artwork to the meta data
|
||||
if (mID3EnableArtwork) {
|
||||
// Load the artwork
|
||||
Bitmap bitmap = ArtworkLoader.loadArtwork(musicTrack, mID3ArtworkMaximumSize);
|
||||
|
||||
if (bitmap != null) {
|
||||
// JPEG is default
|
||||
String mimeType = "image/jpeg";
|
||||
|
||||
// Load the bitmap into a byte array
|
||||
ByteArrayOutputStream artworkDataStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, artworkDataStream);
|
||||
|
||||
// Adds the artwork to the meta data
|
||||
tagID3v2.setAlbumImage(artworkDataStream.toByteArray(), mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
mp3File.setId3v2Tag(tagID3v2);
|
||||
|
||||
|
||||
// Save the file
|
||||
mp3File.save(dest);
|
||||
|
||||
|
||||
// Done
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Logger.getInstance().logError("TrackWriteId3", e.toString());
|
||||
mp3File.setId3v1Tag(tagID3v1);
|
||||
}
|
||||
|
||||
// Failed
|
||||
return false;
|
||||
// It can't be null
|
||||
final ID3v2 tagID3v2;
|
||||
|
||||
// Creates the requested version
|
||||
switch (mID3v2Version) {
|
||||
case ID3v22:
|
||||
tagID3v2 = new ID3v22Tag();
|
||||
break;
|
||||
case ID3v23:
|
||||
tagID3v2 = new ID3v23Tag();
|
||||
break;
|
||||
case ID3v24:
|
||||
tagID3v2 = new ID3v24Tag();
|
||||
break;
|
||||
default:
|
||||
tagID3v2 = null;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Set all tag values
|
||||
tagID3v2.setTitle(musicTrack.getTitle());
|
||||
tagID3v2.setArtist(musicTrack.getArtist());
|
||||
tagID3v2.setAlbum(musicTrack.getAlbum());
|
||||
tagID3v2.setAlbumArtist(musicTrack.getAlbumArtist());
|
||||
tagID3v2.setTrack("" + musicTrack.getTrackNumber());
|
||||
tagID3v2.setPartOfSet("" + musicTrack.getDiscNumber());
|
||||
tagID3v2.setYear(musicTrack.getYear());
|
||||
|
||||
if (!TextUtils.isEmpty(musicTrack.getGenre())) {
|
||||
try {
|
||||
// Maybe the genre is not supported
|
||||
tagID3v2.setGenreDescription(musicTrack.getGenre());
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logger.getInstance().logWarning("TrackWriteID3", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Add the artwork to the meta data
|
||||
if (mID3EnableArtwork) {
|
||||
// Load the artwork
|
||||
Bitmap bitmap = ArtworkLoader.loadArtwork(musicTrack, mID3ArtworkMaximumSize);
|
||||
|
||||
if (bitmap != null) {
|
||||
// JPEG is default
|
||||
String mimeType = "image/jpeg";
|
||||
|
||||
// Load the bitmap into a byte array
|
||||
ByteArrayOutputStream artworkDataStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, artworkDataStream);
|
||||
|
||||
// Adds the artwork to the meta data
|
||||
tagID3v2.setAlbumImage(artworkDataStream.toByteArray(), mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
mp3File.setId3v2Tag(tagID3v2);
|
||||
|
||||
|
||||
// Save the file
|
||||
mp3File.save(dest);
|
||||
|
||||
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a track and save it to a new path
|
||||
*
|
||||
* @param musicTrack The music track
|
||||
* @param src The source mp3 file
|
||||
* @param dest The destination path
|
||||
* @param src The source mp3 file
|
||||
* @param dest The destination path
|
||||
* @return Return if the operation was successful
|
||||
*/
|
||||
private boolean trackEncrypt(MusicTrack musicTrack, String src, String dest) {
|
||||
|
@ -792,9 +845,9 @@ public class PlayMusicManager {
|
|||
/**
|
||||
* Deletes all cache files
|
||||
*/
|
||||
private void cleanUp() {
|
||||
FileTools.fileDelete(getTempPath() + "/final.mp3");
|
||||
FileTools.fileDelete(getTempPath() + "/tmp.mp3");
|
||||
FileTools.fileDelete(getTempPath() + "/crypt.mp3");
|
||||
private void cleanUp(String theUniqueID) {
|
||||
FileTools.fileDelete(getTempPath() + theUniqueID + "_final.mp3");
|
||||
FileTools.fileDelete(getTempPath() + theUniqueID + "_tmp.mp3");
|
||||
FileTools.fileDelete(getTempPath() + theUniqueID + "_crypt.mp3");
|
||||
}
|
||||
}
|
||||
|
|