Merge branch 'release/v0.9.6.0'

This commit is contained in:
Jan Christian Grünhage 2017-03-29 00:31:27 +02:00
commit f28e4cbd48
Signed by: jcgruenhage
GPG key ID: 321A67D9EE8BC3E1
23 changed files with 334 additions and 422 deletions

View file

@ -34,15 +34,31 @@ by [Michael Patricios (mpatric)](https://github.com/mpatric).
### Contributing ### 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, 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. 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. 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: You might want to discuss your ideas with us first though:
### Discuss Ideas #### 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. 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.
I might later set up [BitHub](https://github.com/WhisperSystems/BitHub) for this purpose later (if there are a lot of donations to distribute), but this project is a little small for that, and BitHub works based on the commit count only, which I would like to modify before setting that up.
### Copyright ### Copyright
Copyright (c) 2016 Jan Christian Grünhage. See LICENSE.txt for details. Copyright (c) 2016 Jan Christian Grünhage. See LICENSE.txt for details.

View file

@ -42,5 +42,5 @@ android {
dependencies { dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.2.0' compile 'com.android.support:appcompat-v7:25.3.1'
} }

View file

@ -29,6 +29,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -309,9 +310,7 @@ public class FileTools {
} }
// Remove all blacklisted paths // Remove all blacklisted paths
for (String blacklistPath : mountPointBlacklist) { storages.removeAll(Arrays.asList(mountPointBlacklist));
storages.remove(blacklistPath);
}
// Sort the list // Sort the list
Collections.sort(storages); Collections.sort(storages);

View file

@ -1,6 +1,6 @@
#Sun Jan 22 21:19:51 CET 2017 #Tue Mar 28 12:22:37 CEST 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip

View file

@ -20,8 +20,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
@ -33,8 +31,8 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 25 targetSdkVersion 25
// TODO Change Version with releases // TODO Change Version with releases
versionCode 109 versionCode 110
versionName '0.9.5.2' versionName '0.9.6.0'
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
jackOptions { jackOptions {
@ -58,9 +56,10 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':framework') compile project(':framework')
compile project(':playmusiclib') compile project(':playmusiclib')
compile 'com.android.support:appcompat-v7:25.2.0' compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:support-v4:25.2.0' compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.support:design:25.2.0' compile 'com.android.support:design:25.3.1'
compile 'com.android.support:support-vector-drawable:25.2.0' compile 'com.android.support:support-vector-drawable:25.3.1'
compile 'com.github.paolorotolo:appintro:4.1.0' compile 'com.github.paolorotolo:appintro:4.1.0'
compile 'ly.count.android:sdk:16.12.2'
} }

View file

@ -24,8 +24,9 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" /> <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.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -51,7 +52,7 @@
</activity> </activity>
<activity <activity
android:name=".activities.Intro" android:name=".activities.Intro"
android:label="Play Music Exporter Intro"/> android:label="Play Music Exporter Intro" />
<service android:name=".services.ExportService" /> <service android:name=".services.ExportService" />
<service android:name=".services.ExportAllService" /> <service android:name=".services.ExportAllService" />

View file

@ -4,7 +4,6 @@ import android.Manifest;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Color; import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;

View file

@ -43,9 +43,10 @@ import android.widget.EditText;
import android.widget.Toast; import android.widget.Toast;
import de.arcus.framework.logger.Logger; import de.arcus.framework.logger.Logger;
import de.arcus.framework.crashhandler.CrashHandler;
import de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException; import de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException;
import de.arcus.playmusiclib.exceptions.NoSuperUserException; 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.R;
import re.jcg.playmusicexporter.fragments.MusicTrackListFragment; import re.jcg.playmusicexporter.fragments.MusicTrackListFragment;
import re.jcg.playmusicexporter.fragments.MusicContainerListFragment; import re.jcg.playmusicexporter.fragments.MusicContainerListFragment;
@ -108,8 +109,9 @@ public class MusicContainerListActivity extends AppCompatActivity
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
//Adds the crash handler to this class Countly.sharedInstance().init(this, getString(R.string.countly_url), getString(R.string.countly_token), null, DeviceId.Type.OPEN_UDID);
CrashHandler.addCrashHandler(this); Countly.sharedInstance().enableCrashReporting();
PlayMusicExporterPreferences.init(this); PlayMusicExporterPreferences.init(this);
if (!PlayMusicExporterPreferences.getSetupDone()) { if (!PlayMusicExporterPreferences.getSetupDone()) {
@ -195,11 +197,11 @@ public class MusicContainerListActivity extends AppCompatActivity
} }
} }
} }
/** /**
* Loads the PlayMusicExporter lib and shows the list * Loads the PlayMusicExporter lib and shows the list
*/ */
private void loadPlayMusicExporter() private void loadPlayMusicExporter() {
{
// Gets the running instance // Gets the running instance
mPlayMusicManager = PlayMusicManager.getInstance(); mPlayMusicManager = PlayMusicManager.getInstance();
@ -261,7 +263,7 @@ public class MusicContainerListActivity extends AppCompatActivity
MusicContainerListFragment musicTrackListFragment = (MusicContainerListFragment) getSupportFragmentManager() MusicContainerListFragment musicTrackListFragment = (MusicContainerListFragment) getSupportFragmentManager()
.findFragmentById(R.id.fragment_main); .findFragmentById(R.id.fragment_main);
switch(mViewType) { switch (mViewType) {
case Album: case Album:
// Load all albums to the list // Load all albums to the list
AlbumDataSource dataSourceAlbum = new AlbumDataSource(mPlayMusicManager); AlbumDataSource dataSourceAlbum = new AlbumDataSource(mPlayMusicManager);
@ -331,24 +333,21 @@ public class MusicContainerListActivity extends AppCompatActivity
MenuItem itemRefreshLibrary = menu.findItem(R.id.action_refresh); MenuItem itemRefreshLibrary = menu.findItem(R.id.action_refresh);
itemRefreshLibrary.setOnMenuItemClickListener(item -> itemRefreshLibrary.setOnMenuItemClickListener(item ->
{ {
try try {
{
mPlayMusicManager.reloadDatabase(); mPlayMusicManager.reloadDatabase();
mPlayMusicManager = null; mPlayMusicManager = null;
loadPlayMusicExporter(); loadPlayMusicExporter();
Toast.makeText( this, R.string.database_reloaded, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.database_reloaded, Toast.LENGTH_SHORT).show();
} } catch (NoSuperUserException | CouldNotOpenDatabaseException e) {
catch (NoSuperUserException | CouldNotOpenDatabaseException e) Toast.makeText(this, R.string.dialog_superuser_access_denied_title, Toast.LENGTH_SHORT).show();
{
Toast.makeText( this, R.string.dialog_superuser_access_denied_title, Toast.LENGTH_SHORT).show();
e.printStackTrace(); e.printStackTrace();
} }
return true; return true;
}); });
// Finds the search item and create the search view // Finds the search item and create the search view
MenuItem itemSearch = menu. findItem(R.id.action_search); MenuItem itemSearch = menu.findItem(R.id.action_search);
mSearchView = (SearchView)MenuItemCompat.getActionView(itemSearch); mSearchView = (SearchView) MenuItemCompat.getActionView(itemSearch);
if (mSearchView != null) { if (mSearchView != null) {
// Sets the search listener // Sets the search listener
@ -376,4 +375,16 @@ public class MusicContainerListActivity extends AppCompatActivity
return false; return false;
} }
@Override
public void onStart() {
super.onStart();
Countly.sharedInstance().onStart(this);
}
@Override
public void onStop() {
Countly.sharedInstance().onStop();
super.onStop();
}
} }

View file

@ -46,31 +46,28 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
* A preference value change listener that updates the preference's summary * A preference value change listener that updates the preference's summary
* to reflect its new value. * to reflect its new value.
*/ */
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = (preference, value) -> {
@Override String stringValue = value.toString();
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof ListPreference) { if (preference instanceof ListPreference) {
// For list preferences, look up the correct display value in // For list preferences, look up the correct display value in
// the preference's 'entries' list. // the preference's 'entries' list.
ListPreference listPreference = (ListPreference) preference; ListPreference listPreference = (ListPreference) preference;
int index = listPreference.findIndexOfValue(stringValue); int index = listPreference.findIndexOfValue(stringValue);
// Set the summary to reflect the new value. // Set the summary to reflect the new value.
preference.setSummary( preference.setSummary(
index >= 0 index >= 0
? listPreference.getEntries()[index] ? listPreference.getEntries()[index]
: null); : null);
} else { } else {
// For all other preferences, set the summary to the value's // For all other preferences, set the summary to the value's
// simple string representation. // simple string representation.
preference.setSummary(stringValue); preference.setSummary(stringValue);
}
return true;
} }
return true;
}; };
/** /**
@ -127,15 +124,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
@TargetApi(Build.VERSION_CODES.HONEYCOMB) @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onBuildHeaders(List<Header> target) { public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.pref_headers, target); loadHeadersFromResource(R.xml.pref_headers, target);
if (BuildConfig.DEBUG) {
// Remove the Debug Fragment loadHeadersFromResource(R.xml.pref_debug_header, target);
if (!BuildConfig.DEBUG) {
for (int i = 0; i < target.size(); i++) {
if ("Debug".equals(target.get(i).title)) {
target.remove(i);
break;
}
}
} }
} }
@ -160,19 +150,13 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
setHasOptionsMenu(true); setHasOptionsMenu(true);
findPreference("preference_alba_export_path").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { findPreference("preference_alba_export_path").setOnPreferenceClickListener(preference -> {
@Override startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), REQUEST_CODE_OPEN_DOCUMENT_TREE_ALBA_PATH);
public boolean onPreferenceClick(Preference preference) { return true;
startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), REQUEST_CODE_OPEN_DOCUMENT_TREE_ALBA_PATH);
return true;
}
}); });
findPreference("preference_groups_export_path").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { findPreference("preference_groups_export_path").setOnPreferenceClickListener(preference -> {
@Override startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), REQUEST_CODE_OPEN_DOCUMENT_TREE_GROUPS_PATH);
public boolean onPreferenceClick(Preference preference) { return true;
startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), REQUEST_CODE_OPEN_DOCUMENT_TREE_GROUPS_PATH);
return true;
}
}); });
} }
@ -246,12 +230,9 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
} }
private void setupOnClickListeners() { private void setupOnClickListeners() {
findPreference(PlayMusicExporterPreferences.AUTO_EXPORT_PATH).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { findPreference(PlayMusicExporterPreferences.AUTO_EXPORT_PATH).setOnPreferenceClickListener(preference -> {
@Override startActivityForResult(new Intent("android.intent.action.OPEN_DOCUMENT_TREE"), REQUEST_CODE_OPEN_DOCUMENT_TREE_AUTO_PATH);
public boolean onPreferenceClick(Preference preference) { return true;
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 //For every setting that is used to define the export job, setup a listener that
@ -262,12 +243,9 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
} }
private void setupRescheduler(String preference) { private void setupRescheduler(String preference) {
findPreference(preference).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { findPreference(preference).setOnPreferenceClickListener(preference1 -> {
@Override ExportAllJob.scheduleExport(getContext());
public boolean onPreferenceClick(Preference preference) { return false;
ExportAllJob.scheduleExport(getContext());
return false;
}
}); });
} }

View file

@ -79,10 +79,7 @@ public class MusicContainerListFragment extends ListFragment {
* A dummy implementation of the {@link Callbacks} interface that does * A dummy implementation of the {@link Callbacks} interface that does
* nothing. Used only when this fragment is not attached to an activity. * nothing. Used only when this fragment is not attached to an activity.
*/ */
private static Callbacks sDummyCallbacks = new Callbacks() { private static Callbacks sDummyCallbacks = musicTrackList -> {
@Override
public void onItemSelected(MusicTrackList musicTrackList) {
}
}; };
private MusicContainerListAdapter mMusicTrackListAdapter; private MusicContainerListAdapter mMusicTrackListAdapter;
@ -112,9 +109,7 @@ public class MusicContainerListFragment extends ListFragment {
// Null check // Null check
if (list != null) { if (list != null) {
// Copy the list // Copy the list
for (MusicTrackList musicTrackList : list) { newList.addAll(list);
newList.add(musicTrackList);
}
} }
// Set the list in the adapter // Set the list in the adapter
@ -145,7 +140,8 @@ public class MusicContainerListFragment extends ListFragment {
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION)); setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
} }
} }
// DEPRECATED
// DEPRECATED
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);

View file

@ -32,7 +32,6 @@ import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
@ -197,43 +196,36 @@ public class MusicTrackListFragment extends Fragment {
mListView.setAdapter(mMusicTrackAdapter); mListView.setAdapter(mMusicTrackAdapter);
// Click on one list item // Click on one list item
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { mListView.setOnItemClickListener((parent, view, position, id) -> {
@Override // The header is not clicked
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (position > 0) {
// The header is not clicked // We need to subtract the header view
if (position > 0) { position -= 1;
// We need to subtract the header view
position -= 1;
// Gets the selected track // Gets the selected track
MusicTrack musicTrack = mMusicTrackAdapter.getItem(position); MusicTrack musicTrack = mMusicTrackAdapter.getItem(position);
// Toggle the track // Toggle the track
selectTrack(musicTrack, view, TrackSelectionState.Toggle); selectTrack(musicTrack, view, TrackSelectionState.Toggle);
}
} }
}); });
// The floating action button // The floating action button
mFloatingButtonExport = (FloatingActionButton) rootView.findViewById(R.id.floating_button_export); mFloatingButtonExport = (FloatingActionButton) rootView.findViewById(R.id.floating_button_export);
mFloatingButtonExport.setOnClickListener(new View.OnClickListener() { mFloatingButtonExport.setOnClickListener(v -> {
@Override m_CPULock.acquire();
public void onClick(View v) // Export all selected tracks
{ for (SelectedTrack selectedTrack : SelectedTrackList.getInstance().getSelectedItems()) {
m_CPULock.acquire(); selectedTrack.export(getActivity());
// 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);
} }
if ( m_CPULock.isHeld())
{
m_CPULock.release();
}
// Clear the selection
SelectedTrackList.getInstance().clear(true);
}); });
updateFloatingButton(); updateFloatingButton();
} }

View file

@ -22,7 +22,6 @@
package re.jcg.playmusicexporter.fragments; package re.jcg.playmusicexporter.fragments;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
@ -160,47 +159,24 @@ public class NavigationDrawerFragment extends Fragment {
setViewType(mViewType); setViewType(mViewType);
// Click on album // Click on album
mButtonTypeAlbum.setOnClickListener(new View.OnClickListener() { mButtonTypeAlbum.setOnClickListener(v -> setViewType(ViewType.Album));
@Override
public void onClick(View v) {
setViewType(ViewType.Album);
}
});
// Click on artist // Click on artist
mButtonTypeArtist.setOnClickListener(new View.OnClickListener() { mButtonTypeArtist.setOnClickListener(v -> setViewType(ViewType.Artist));
@Override
public void onClick(View v) {
setViewType(ViewType.Artist);
}
});
// Click on playlist // Click on playlist
mButtonTypePlaylist.setOnClickListener(new View.OnClickListener() { mButtonTypePlaylist.setOnClickListener(v -> setViewType(ViewType.Playlist));
@Override
public void onClick(View v) {
setViewType(ViewType.Playlist);
}
});
// Click on rated // Click on rated
mButtonTypeRated.setOnClickListener(new View.OnClickListener() { mButtonTypeRated.setOnClickListener(v -> setViewType(ViewType.Rated));
@Override
public void onClick(View v) {
setViewType(ViewType.Rated);
}
});
// Click on settings // Click on settings
mButtonSettings.setOnClickListener(new View.OnClickListener() { mButtonSettings.setOnClickListener(v -> {
@Override Intent intentSettings = new Intent(getActivity(), SettingsActivity.class);
public void onClick(View v) { startActivity(intentSettings);
Intent intentSettings = new Intent(getActivity(), SettingsActivity.class);
startActivity(intentSettings);
// Close the drawer // Close the drawer
mDrawerLayout.closeDrawers(); mDrawerLayout.closeDrawers();
}
}); });
// Color the settings button // Color the settings button
@ -307,12 +283,7 @@ public class NavigationDrawerFragment extends Fragment {
} }
// Defer code dependent on restoration of previous instance state. // Defer code dependent on restoration of previous instance state.
mDrawerLayout.post(new Runnable() { mDrawerLayout.post(mDrawerToggle::syncState);
@Override
public void run() {
mDrawerToggle.syncState();
}
});
mDrawerLayout.setDrawerListener(mDrawerToggle); mDrawerLayout.setDrawerListener(mDrawerToggle);
} }

View file

@ -9,6 +9,7 @@ import android.util.Log;
import java.util.List; import java.util.List;
import ly.count.android.sdk.Countly;
import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences; import re.jcg.playmusicexporter.settings.PlayMusicExporterPreferences;
import re.jcg.playmusicexporter.utils.MusicPathBuilder; import re.jcg.playmusicexporter.utils.MusicPathBuilder;
import de.arcus.playmusiclib.PlayMusicManager; import de.arcus.playmusiclib.PlayMusicManager;
@ -34,8 +35,7 @@ public class ExportAllService extends IntentService {
Log.i(TAG, "Intent sent!"); Log.i(TAG, "Intent sent!");
} }
public ExportAllService() public ExportAllService() {
{
super("AutoGPME-ExportService"); super("AutoGPME-ExportService");
} }
@ -75,10 +75,11 @@ public class ExportAllService extends IntentService {
try { try {
if (lPlayMusicManager.exportMusicTrack(lTrack, lUri, lPath, PlayMusicExporterPreferences.getFileOverwritePreference())) { if (lPlayMusicManager.exportMusicTrack(lTrack, lUri, lPath, PlayMusicExporterPreferences.getFileOverwritePreference())) {
Log.i(TAG, "Exported Music Track: " + getStringForTrack(lTrack)); Log.i(TAG, "Exported Music Track: " + getStringForTrack(lTrack));
Countly.sharedInstance().recordEvent("Exported Song", 1);
} else { } else {
Log.i(TAG, "Failed to export Music Track: " + getStringForTrack(lTrack)); Log.i(TAG, "Failed to export Music Track: " + getStringForTrack(lTrack));
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
if (e.getMessage().contains("Invalid URI:")) { if (e.getMessage().contains("Invalid URI:")) {
/* /*
@ -88,19 +89,14 @@ public class ExportAllService extends IntentService {
*/ */
Log.i(TAG, "Automatic export failed, because the URI is invalid."); Log.i(TAG, "Automatic export failed, because the URI is invalid.");
} else throw e; } else throw e;
} } catch (Exception e) {
finally Countly.sharedInstance().logException(e);
{ e.printStackTrace();
if ( CPULock.isHeld())
{
CPULock.release();
}
} }
} }
} }
} }
if ( CPULock.isHeld()) if (CPULock.isHeld()) {
{
CPULock.release(); CPULock.release();
} }
} }

View file

@ -31,6 +31,7 @@ import android.os.Bundle;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import de.arcus.framework.logger.Logger; import de.arcus.framework.logger.Logger;
import ly.count.android.sdk.Countly;
import re.jcg.playmusicexporter.R; import re.jcg.playmusicexporter.R;
import de.arcus.playmusiclib.PlayMusicManager; import de.arcus.playmusiclib.PlayMusicManager;
import de.arcus.playmusiclib.datasources.MusicTrackDataSource; import de.arcus.playmusiclib.datasources.MusicTrackDataSource;
@ -200,9 +201,16 @@ public class ExportService extends IntentService {
updateNotification(); updateNotification();
// Exports the song // Exports the song
if(!playMusicManager.exportMusicTrack(mTrackCurrent, uri, path, PlayMusicExporterPreferences.getFileOverwritePreference())) { try {
// Export failed if (playMusicManager.exportMusicTrack(mTrackCurrent, uri, path, PlayMusicExporterPreferences.getFileOverwritePreference())) {
mTracksFailed ++; Countly.sharedInstance().recordEvent("Exported Song", 1);
} else {
// Export failed
mTracksFailed ++;
}
} catch (Exception e) {
Countly.sharedInstance().logException(e);
e.printStackTrace();
} }
} else { } else {
// Export failed // Export failed

View file

@ -22,14 +22,12 @@
package re.jcg.playmusicexporter.utils; package re.jcg.playmusicexporter.utils;
import android.graphics.Bitmap;
import android.widget.ImageView; import android.widget.ImageView;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import re.jcg.playmusicexporter.R; import re.jcg.playmusicexporter.R;
import de.arcus.playmusiclib.ArtworkLoader; import de.arcus.playmusiclib.ArtworkLoader;
import de.arcus.playmusiclib.ArtworkLoaderCallback;
import de.arcus.playmusiclib.items.ArtworkEntry; 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); maximalArtworkSize = imageViewDefault.getContext().getResources().getDimensionPixelSize(R.dimen.music_track_artwork_loading_size);
// Sets the bitmap in the UI thread // Sets the bitmap in the UI thread
Runnable runnable = new Runnable() { Runnable runnable = () -> {
@Override // Default icon
public void run() { imageViewDefault.setImageResource(mDefaultImage);
// Default icon
imageViewDefault.setImageResource(mDefaultImage);
}
}; };
imageViewDefault.post(runnable); imageViewDefault.post(runnable);
} }
@ -138,36 +133,30 @@ public class ArtworkViewLoader {
mIsLoading = true; mIsLoading = true;
// Load the artwork // Load the artwork
ArtworkLoader.loadArtworkAsync(mArtworkEntry, maximalArtworkSize, new ArtworkLoaderCallback() { ArtworkLoader.loadArtworkAsync(mArtworkEntry, maximalArtworkSize, bitmap -> {
@Override final ImageView imageView = mImageView.get();
public void onFinished(final Bitmap bitmap) {
final ImageView imageView = mImageView.get();
if (imageViewDefault != null) { if (imageViewDefault != null) {
// Sets the bitmap in the UI thread // Sets the bitmap in the UI thread
Runnable runnable = new Runnable() { Runnable runnable = () -> {
@Override // Bitmap is valid
public void run() { if (bitmap != null)
// Bitmap is valid imageView.setImageBitmap(bitmap);
if (bitmap != null) else
imageView.setImageBitmap(bitmap); imageView.setImageResource(mDefaultImage);
else };
imageView.setImageResource(mDefaultImage); imageView.post(runnable);
} }
};
imageView.post(runnable);
}
// Loading is done // Loading is done
mIsLoading = false; mIsLoading = false;
// Loads the next image // Loads the next image
if (mNewArtworkEntry != null) { if (mNewArtworkEntry != null) {
mArtworkEntry = mNewArtworkEntry; mArtworkEntry = mNewArtworkEntry;
mNewArtworkEntry = null; mNewArtworkEntry = null;
loadImage(); loadImage();
}
} }
}); });
} }

View file

@ -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>

View file

@ -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>

View file

@ -21,7 +21,11 @@
~ THE SOFTWARE. ~ THE SOFTWARE.
--> -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" <ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:color="@color/button_navigation_drawer_hover">
<solid android:color="@color/button_navigation_drawer_normal"/> <item>
</shape> <shape android:shape="rectangle">
<solid android:color="@color/button_navigation_drawer_normal"/>
</shape>
</item>
</ripple>

View file

@ -21,7 +21,11 @@
~ THE SOFTWARE. ~ THE SOFTWARE.
--> -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" <ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:color="@color/button_navigation_drawer_hover">
<solid android:color="@color/button_navigation_drawer_selected"/> <item>
</shape> <shape android:shape="rectangle">
<solid android:color="@color/button_navigation_drawer_selected"/>
</shape>
</item>
</ripple>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="countly_url">https://countly.jcg.re/</string>
<string name="countly_token">82a1d9405388c4dd73dc9835f84c59cf4274086d</string>
</resources>

View 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>

View file

@ -16,9 +16,5 @@
android:fragment="re.jcg.playmusicexporter.activities.SettingsActivity$AboutPreferenceFragment" android:fragment="re.jcg.playmusicexporter.activities.SettingsActivity$AboutPreferenceFragment"
android:icon="@drawable/ic_info_black_24dp" android:icon="@drawable/ic_info_black_24dp"
android:title="@string/pref_header_about" /> android:title="@string/pref_header_about" />
<header
android:fragment="re.jcg.playmusicexporter.activities.SettingsActivity$DebugPreferenceFragment"
android:icon="@drawable/ic_action_settings"
android:title="@string/pref_header_debug" />
</preference-headers> </preference-headers>

View file

@ -25,7 +25,6 @@ package de.arcus.playmusiclib;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.SQLException; import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -33,10 +32,8 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.support.v4.content.FileProvider;
import android.support.v4.provider.DocumentFile; import android.support.v4.provider.DocumentFile;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import com.mpatric.mp3agic.ID3v1Genres; import com.mpatric.mp3agic.ID3v1Genres;
import com.mpatric.mp3agic.ID3v1Tag; import com.mpatric.mp3agic.ID3v1Tag;
@ -44,7 +41,10 @@ import com.mpatric.mp3agic.ID3v2;
import com.mpatric.mp3agic.ID3v22Tag; import com.mpatric.mp3agic.ID3v22Tag;
import com.mpatric.mp3agic.ID3v23Tag; import com.mpatric.mp3agic.ID3v23Tag;
import com.mpatric.mp3agic.ID3v24Tag; import com.mpatric.mp3agic.ID3v24Tag;
import com.mpatric.mp3agic.InvalidDataException;
import com.mpatric.mp3agic.Mp3File; import com.mpatric.mp3agic.Mp3File;
import com.mpatric.mp3agic.NotSupportedException;
import com.mpatric.mp3agic.UnsupportedTagException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@ -138,6 +138,7 @@ public class PlayMusicManager {
/** /**
* The database will be copied to a temp folder to access from the app * The database will be copied to a temp folder to access from the app
*
* @return Gets the temp path to the database * @return Gets the temp path to the database
*/ */
private String getTempDatabasePath() { private String getTempDatabasePath() {
@ -297,6 +298,7 @@ public class PlayMusicManager {
/** /**
* Creates a new PlayMusic manager * Creates a new PlayMusic manager
*
* @param context App context * @param context App context
*/ */
public PlayMusicManager(Context context) { public PlayMusicManager(Context context) {
@ -307,8 +309,9 @@ public class PlayMusicManager {
/** /**
* Loads all needed information and opens the database * 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 * @throws CouldNotOpenDatabaseException Could not open the database
*/ */
public void startUp() throws PlayMusicNotFoundException, NoSuperUserException, CouldNotOpenDatabaseException { public void startUp() throws PlayMusicNotFoundException, NoSuperUserException, CouldNotOpenDatabaseException {
@ -345,7 +348,8 @@ public class PlayMusicManager {
/** /**
* Copies the database to a temp directory and opens it * 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 * @throws de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException Could not open the database
*/ */
private void loadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException { private void loadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
@ -370,7 +374,8 @@ public class PlayMusicManager {
/** /**
* Reloads the database from PlayMusic * Reloads the database from PlayMusic
* @throws NoSuperUserException No super user permissions *
* @throws NoSuperUserException No super user permissions
* @throws CouldNotOpenDatabaseException Could not open the database * @throws CouldNotOpenDatabaseException Could not open the database
*/ */
public void reloadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException { public void reloadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
@ -403,6 +408,7 @@ public class PlayMusicManager {
/** /**
* Gets the path to the music track * Gets the path to the music track
*
* @param localCopyPath The local copy path * @param localCopyPath The local copy path
* @return The path to the music file * @return The path to the music file
*/ */
@ -437,6 +443,7 @@ public class PlayMusicManager {
/** /**
* Gets the full path to the artwork * Gets the full path to the artwork
*
* @param artworkPath The artwork path * @param artworkPath The artwork path
* @return The full path to the artwork * @return The full path to the artwork
*/ */
@ -469,12 +476,13 @@ public class PlayMusicManager {
/** /**
* Exports a track to the sd card * 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 forceOverwrite Forces overwrite of the destination file * @param dest The destination path
* @param forceOverwrite Forces overwrite of the destination file
* @return Returns whether the export was successful * @return Returns whether the export was successful
*/ */
public boolean exportMusicTrack(MusicTrack musicTrack, String dest, boolean forceOverwrite ) { public boolean exportMusicTrack(MusicTrack musicTrack, String dest, boolean forceOverwrite) throws InvalidDataException, IOException, UnsupportedTagException, NotSupportedException {
// Creates the destination directory // Creates the destination directory
File directory = new File(dest).getParentFile(); File directory = new File(dest).getParentFile();
@ -487,12 +495,13 @@ public class PlayMusicManager {
/** /**
* Exports a track to the sd card * 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 forceOverwrite Forces overwrite of the destination file * @param uri The document tree
* @param forceOverwrite Forces overwrite of the destination file
* @return Returns whether the export was successful * @return Returns whether the export was successful
*/ */
public boolean exportMusicTrack(MusicTrack musicTrack, Uri uri, String path, boolean forceOverwrite) { public boolean exportMusicTrack(MusicTrack musicTrack, Uri uri, String path, boolean forceOverwrite) throws InvalidDataException, IOException, UnsupportedTagException, NotSupportedException {
// Check for null // Check for null
if (musicTrack == null) return false; if (musicTrack == null) return false;
@ -504,10 +513,9 @@ public class PlayMusicManager {
String uniqueID = UUID.randomUUID().toString(); String uniqueID = UUID.randomUUID().toString();
if ( forceOverwrite || !isAlreadyThere(uri, path) ) if (forceOverwrite || !isAlreadyThere(uri, path)) {
{
String fileTmp = getTempPath() + uniqueID +"_tmp.mp3"; String fileTmp = getTempPath() + uniqueID + "_tmp.mp3";
// Copy to temp path failed // Copy to temp path failed
if (!SuperUserTools.fileCopy(srcFile, fileTmp)) if (!SuperUserTools.fileCopy(srcFile, fileTmp))
@ -515,7 +523,7 @@ public class PlayMusicManager {
// Encrypt the file // Encrypt the file
if (musicTrack.isEncoded()) { if (musicTrack.isEncoded()) {
String fileTmpCrypt = getTempPath() + uniqueID +"_crypt.mp3"; String fileTmpCrypt = getTempPath() + uniqueID + "_crypt.mp3";
// Encrypts the file // Encrypts the file
if (trackEncrypt(musicTrack, fileTmp, fileTmpCrypt)) { if (trackEncrypt(musicTrack, fileTmp, fileTmpCrypt)) {
@ -530,7 +538,6 @@ public class PlayMusicManager {
} }
String dest; String dest;
Uri copyUri = null; Uri copyUri = null;
if (uri.toString().startsWith("file://")) { if (uri.toString().startsWith("file://")) {
@ -541,19 +548,19 @@ public class PlayMusicManager {
FileTools.directoryCreate(parentDirectory); FileTools.directoryCreate(parentDirectory);
} else { } else {
// Complex uri (Lollipop) // Complex uri (Lollipop)
dest = getTempPath() + uniqueID +"_final.mp3"; dest = getTempPath() + uniqueID + "_final.mp3";
// The root // The root
DocumentFile document = DocumentFile.fromTreeUri(mContext, uri); DocumentFile document = DocumentFile.fromTreeUri(mContext, uri);
// Creates the subdirectories // Creates the subdirectories
String[] directories = path.split("\\/"); String[] directories = path.split("/");
for(int i=0; i<directories.length - 1; i++) { for (int i = 0; i < directories.length - 1; i++) {
String directoryName = directories[i]; String directoryName = directories[i];
boolean found = false; boolean found = false;
// Search all sub elements // Search all sub elements
for (DocumentFile subDocument: document.listFiles()) { for (DocumentFile subDocument : document.listFiles()) {
// Directory exists // Directory exists
if (subDocument.isDirectory() && subDocument.getName().equalsIgnoreCase(directoryName)) { if (subDocument.isDirectory() && subDocument.getName().equalsIgnoreCase(directoryName)) {
document = subDocument; document = subDocument;
@ -571,12 +578,12 @@ public class PlayMusicManager {
// Gets the filename // Gets the filename
String filename = directories[directories.length - 1]; String filename = directories[directories.length - 1];
for (DocumentFile subDocument: document.listFiles()) { for (DocumentFile subDocument : document.listFiles()) {
// Directory exists // Directory exists
if (subDocument.isFile() ){ if (subDocument.isFile()) {
if ( filename != null && subDocument.getName().equalsIgnoreCase(filename)) { if (filename != null && subDocument.getName().equalsIgnoreCase(filename)) {
// Delete the file // Delete the file
if ( forceOverwrite ) { if (forceOverwrite) {
Logger.getInstance().logWarning("ExportMusicTrack", "(forceOverwrite) Deleting original file: " + filename); Logger.getInstance().logWarning("ExportMusicTrack", "(forceOverwrite) Deleting original file: " + filename);
} }
subDocument.delete(); subDocument.delete();
@ -660,7 +667,7 @@ public class PlayMusicManager {
//new MediaScanner(mContext, dest); //new MediaScanner(mContext, dest);
} else { } else {
Logger.getInstance().logInfo("exportMusicTrack", path + " already exists, skipping." ); Logger.getInstance().logInfo("exportMusicTrack", path + " already exists, skipping.");
} }
// Done // Done
@ -669,9 +676,10 @@ public class PlayMusicManager {
/** /**
* Checks if the destination file already exists * Checks if the destination file already exists
* @param pUri the source file *
* @param pUri the source file
* @param pPath The destination * @param pPath The destination
* return true if the file already exists * return true if the file already exists
*/ */
private boolean isAlreadyThere(Uri pUri, String pPath) { private boolean isAlreadyThere(Uri pUri, String pPath) {
if (pUri.toString().startsWith("file://")) { if (pUri.toString().startsWith("file://")) {
@ -683,8 +691,8 @@ public class PlayMusicManager {
for (String lDisplayName : pPath.split("/")) { for (String lDisplayName : pPath.split("/")) {
if (lDocumentFile.findFile(lDisplayName) != null) { if (lDocumentFile.findFile(lDisplayName) != null) {
lDocumentFile = lDocumentFile.findFile(lDisplayName); lDocumentFile = lDocumentFile.findFile(lDisplayName);
if ( lDocumentFile.length() == 0 ) { if (lDocumentFile.length() == 0) {
if ( !lDocumentFile.isDirectory() ) { if (!lDocumentFile.isDirectory()) {
Logger.getInstance().logInfo("isAlreadyThere", pPath + " File exists, but is 0 bytes in size."); Logger.getInstance().logInfo("isAlreadyThere", pPath + " File exists, but is 0 bytes in size.");
} }
} }
@ -699,122 +707,117 @@ public class PlayMusicManager {
/** /**
* Copies the music file to a new path and adds the mp3 meta data * Copies the music file to a new path and adds the mp3 meta data
*
* @param musicTrack Track information * @param musicTrack Track information
* @param src The source mp3 file * @param src The source mp3 file
* @param dest The destination path * @param dest The destination path
* return Return if the operation was successful * return Return if the operation was successful
*/ */
private boolean trackWriteID3(MusicTrack musicTrack, String src, String dest) { private boolean trackWriteID3(MusicTrack musicTrack, String src, String dest) throws InvalidDataException, IOException, UnsupportedTagException, NotSupportedException {
try { // Opens the mp3
// Opens the mp3 Mp3File mp3File = new Mp3File(src);
Mp3File mp3File = new Mp3File(src);
// Removes all existing tags // Removes all existing tags
mp3File.removeId3v1Tag(); mp3File.removeId3v1Tag();
mp3File.removeId3v2Tag(); mp3File.removeId3v2Tag();
mp3File.removeCustomTag(); 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;
}
}
mp3File.setId3v1Tag(tagID3v1);
}
// 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;
}
// We want to add a fallback ID3v1 tag
if (mID3EnableFallback) {
// Create a new tag with ID3v1
ID3v1Tag tagID3v1 = new ID3v1Tag();
// Set all tag values // Set all tag values
tagID3v2.setTitle(musicTrack.getTitle()); tagID3v1.setTrack(musicTrack.getTitle());
tagID3v2.setArtist(musicTrack.getArtist()); tagID3v1.setArtist(musicTrack.getArtist());
tagID3v2.setAlbum(musicTrack.getAlbum()); tagID3v1.setAlbum(musicTrack.getAlbum());
tagID3v2.setAlbumArtist(musicTrack.getAlbumArtist()); tagID3v1.setYear(musicTrack.getYear());
tagID3v2.setTrack("" + musicTrack.getTrackNumber());
tagID3v2.setPartOfSet("" + musicTrack.getDiscNumber());
tagID3v2.setYear(musicTrack.getYear());
if (!TextUtils.isEmpty(musicTrack.getGenre())) { // Search the genre
try { for (int n = 0; n < ID3v1Genres.GENRES.length; n++) {
// Maybe the genre is not supported // Genre found
tagID3v2.setGenreDescription(musicTrack.getGenre()); if (ID3v1Genres.GENRES[n].equals(musicTrack.getGenre())) {
} catch (IllegalArgumentException e) { tagID3v1.setGenre(n);
Logger.getInstance().logWarning("TrackWriteID3", e.getMessage()); break;
} }
} }
// Add the artwork to the meta data mp3File.setId3v1Tag(tagID3v1);
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());
} }
// Failed // It can't be null
return false; 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 * Encrypts a track and save it to a new path
*
* @param musicTrack The music track * @param musicTrack The music track
* @param src The source mp3 file * @param src The source mp3 file
* @param dest The destination path * @param dest The destination path
* @return Return if the operation was successful * @return Return if the operation was successful
*/ */
private boolean trackEncrypt(MusicTrack musicTrack, String src, String dest) { private boolean trackEncrypt(MusicTrack musicTrack, String src, String dest) {
@ -843,8 +846,8 @@ public class PlayMusicManager {
* Deletes all cache files * Deletes all cache files
*/ */
private void cleanUp(String theUniqueID) { private void cleanUp(String theUniqueID) {
FileTools.fileDelete( getTempPath() + theUniqueID +"_final.mp3"); FileTools.fileDelete(getTempPath() + theUniqueID + "_final.mp3");
FileTools.fileDelete( getTempPath() + theUniqueID +"_tmp.mp3"); FileTools.fileDelete(getTempPath() + theUniqueID + "_tmp.mp3");
FileTools.fileDelete( getTempPath() + theUniqueID +"_crypt.mp3"); FileTools.fileDelete(getTempPath() + theUniqueID + "_crypt.mp3");
} }
} }