Use the new AppCompatActivity

Adds an artwork loader to load artworks from the cache or the internet
Improve the selection
This commit is contained in:
David Schulte 2015-05-14 11:42:57 +02:00
parent 608ebc2034
commit c36675f639
40 changed files with 1408 additions and 1005 deletions

View file

@ -1,11 +1,11 @@
<component name="libraryTable">
<library name="appcompat-v7-22.1.1">
<ANNOTATIONS>
<root url="jar://$PROJECT_DIR$/playmusicexporter/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/annotations.zip!/" />
<root url="jar://$PROJECT_DIR$/framework/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/annotations.zip!/" />
</ANNOTATIONS>
<CLASSES>
<root url="jar://$PROJECT_DIR$/playmusicexporter/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/classes.jar!/" />
<root url="file://$PROJECT_DIR$/playmusicexporter/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/res" />
<root url="file://$PROJECT_DIR$/framework/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/res" />
<root url="jar://$PROJECT_DIR$/framework/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/classes.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>

View file

@ -1,9 +1,9 @@
<component name="libraryTable">
<library name="support-v4-22.1.1">
<CLASSES>
<root url="jar://$PROJECT_DIR$/playmusicexporter/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/libs/internal_impl-22.1.1.jar!/" />
<root url="file://$PROJECT_DIR$/playmusicexporter/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/res" />
<root url="jar://$PROJECT_DIR$/playmusicexporter/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/classes.jar!/" />
<root url="file://$PROJECT_DIR$/framework/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/res" />
<root url="jar://$PROJECT_DIR$/framework/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/classes.jar!/" />
<root url="jar://$PROJECT_DIR$/framework/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/libs/internal_impl-22.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>

View file

@ -20,14 +20,6 @@
* THE SOFTWARE.
*/
apply plugin: 'com.android.library'
android {
@ -50,5 +42,5 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.android.support:appcompat-v7:22.1.1'
}

View file

@ -87,8 +87,8 @@
</content>
<orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="appcompat-v7-21.0.3" level="project" />
<orderEntry type="library" exported="" name="support-annotations-21.0.3" level="project" />
<orderEntry type="library" exported="" name="support-v4-21.0.3" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-22.1.1" level="project" />
<orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" />
<orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" />
</component>
</module>

View file

@ -28,8 +28,8 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.v4.app.ShareCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
@ -38,7 +38,7 @@ import android.widget.TextView;
import de.arcus.framework.logger.Logger;
import de.arcus.framework.R;
public class CrashActivity extends ActionBarActivity {
public class CrashActivity extends AppCompatActivity {
// Extra flags
public static final String EXTRA_FLAG_CRASH_MESSAGE = "CRASH_TITLE";
public static final String EXTRA_FLAG_CRASH_LOG = "CRASH_LOG";

View file

@ -23,7 +23,8 @@
package de.arcus.framework.activities;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@ -32,7 +33,7 @@ import de.arcus.framework.R;
/**
* Activity to browse for a directory
*/
public class DirectoryBrowserActivity extends ActionBarActivity {
public class DirectoryBrowserActivity extends AppCompatActivity {
// The intent extra names
public final static String EXTRA_PATH = "path";
public final static String EXTRA_TITLE = "title";
@ -66,9 +67,13 @@ public class DirectoryBrowserActivity extends ActionBarActivity {
mPath = bundle.getString(EXTRA_PATH);
}
// Set the title
getSupportActionBar().setTitle(mTitle);
getSupportActionBar().setSubtitle(mPath);
// Setup the actionbar
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
// Set the title
actionBar.setTitle(mTitle);
actionBar.setSubtitle(mPath);
}

View file

@ -0,0 +1,80 @@
/*
* 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.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/**
* Class for general image functions
*/
public class ImageTools {
/**
* Loads a bitmap and scale it down to a maximum size
* @param bytes The image data
* @param width The maximal width
* @param height The maximal height
* @return The loaded bitmap
*/
public static Bitmap decodeByteArraySubsampled(byte[] bytes, int width, int height) {
// Loads only the image dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
// If one side is 0 we load the original bitmap size
if (width > 0 && height > 0) {
// Calculate inSampleSize
options.inSampleSize = calculateSampleSize(options, width, height);
}
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
}
/**
* Given the bitmap size and View size calculate a subsampling size (powers of 2)
* @param options The image optione with the originial size
* @param width The maximal width
* @param height The maximal height
* @return The subsampling size
*/
private static int calculateSampleSize( BitmapFactory.Options options, int width, int height) {
int sampleSize = 1; //Default subsampling size
// The image is larger than required, we need to scale it down
if (options.outHeight > height || options.outWidth > width) {
// Gets the half resolutions
final int halfHeight = options.outHeight / 2;
final int halfWidth = options.outWidth / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / sampleSize) > height
&& (halfWidth / sampleSize) > width) {
sampleSize *= 2;
}
}
return sampleSize;
}
}

View file

@ -23,7 +23,7 @@
package de.arcus.framework.utils;
import android.support.annotation.ColorRes;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.view.View;
@ -43,7 +43,7 @@ public abstract class SelectionList<T> {
* The activity
* We need the activity to show the action mode when some items are selected
*/
private ActionBarActivity mActivity;
private AppCompatActivity mActivity;
/**
* The action mode
@ -65,7 +65,7 @@ public abstract class SelectionList<T> {
* Gets the activity
* @return Returns the activity
*/
public ActionBarActivity getActivity() {
public AppCompatActivity getActivity() {
return mActivity;
}
@ -84,7 +84,7 @@ public abstract class SelectionList<T> {
* Sets up the action mode for this selection list
* @param activity The activity
*/
public void setupActionMode(ActionBarActivity activity) {
public void setupActionMode(AppCompatActivity activity) {
mActivity = activity;
mActionModeCallback = createActionMode(activity);
@ -172,6 +172,13 @@ public abstract class SelectionList<T> {
updateActionModeMenu();
}
/**
* Clears the action mode
*/
public void clearActionMode() {
mActionMode = null;
}
/**
* Call this after the view was created.
* This will change the background color.
@ -222,7 +229,7 @@ public abstract class SelectionList<T> {
* @param activity The new activity
* @return Returns the action mode callback
*/
protected abstract ActionMode.Callback createActionMode(ActionBarActivity activity);
protected abstract ActionMode.Callback createActionMode(AppCompatActivity activity);
/**
* Gets whether the item is selected

View file

@ -25,6 +25,7 @@
package="de.arcus.playmusicexporter2" >
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
@ -33,7 +34,7 @@
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity
android:name=".activities.MusicTrackListActivity"
android:name=".activities.MusicContainerListActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -42,20 +43,20 @@
</intent-filter>
</activity>
<activity
android:name=".activities.MusicTrackDetailActivity"
android:name=".activities.MusicTrackListActivity"
android:label="@string/title_track_detail"
android:parentActivityName=".activities.MusicTrackListActivity" >
android:parentActivityName=".activities.MusicContainerListActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MusicTrackListActivity" />
android:value=".activities.MusicContainerListActivity" />
</activity>
<activity
android:name=".activities.SettingsActivity"
android:label="@string/title_settings"
android:parentActivityName=".activities.MusicTrackListActivity" >
android:parentActivityName=".activities.MusicContainerListActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MusicTrackListActivity" />
android:value=".activities.MusicContainerListActivity" />
</activity>
<service android:name="de.arcus.playmusicexporter2.services.ExportService"/>

View file

@ -29,8 +29,8 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.activities.MusicTrackDetailActivity;
import de.arcus.playmusicexporter2.activities.MusicTrackListActivity;
import de.arcus.playmusicexporter2.activities.MusicContainerListActivity;
import de.arcus.playmusicexporter2.items.SelectedTrack;
import de.arcus.playmusicexporter2.items.SelectedTrackList;
@ -59,6 +59,9 @@ public class ActionModeTitle implements ActionMode.Callback {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.action_mode_selection, menu);
//mode.setSubtitle("TEST");
mode.setTitleOptionalHint(true);
return true;
}
@ -82,6 +85,7 @@ public class ActionModeTitle implements ActionMode.Callback {
selectedTrack.export(mContext);
}
// Close the action mode
mode.finish();
return true;
default:
@ -95,15 +99,15 @@ public class ActionModeTitle implements ActionMode.Callback {
private void updateViews()
{
// We are in the album list
if (mSelectionList.getActivity() instanceof MusicTrackListActivity) {
MusicTrackListActivity trackListActivity = (MusicTrackListActivity)mSelectionList.getActivity();
if (mSelectionList.getActivity() instanceof MusicContainerListActivity) {
MusicContainerListActivity trackListActivity = (MusicContainerListActivity)mSelectionList.getActivity();
trackListActivity.updateLists();
}
// We are in the track list
if (mSelectionList.getActivity() instanceof MusicTrackDetailActivity) {
MusicTrackDetailActivity trackDetailActivity = (MusicTrackDetailActivity)mSelectionList.getActivity();
if (mSelectionList.getActivity() instanceof MusicTrackListActivity) {
MusicTrackListActivity trackDetailActivity = (MusicTrackListActivity)mSelectionList.getActivity();
trackDetailActivity.updateLists();
}
@ -112,10 +116,20 @@ public class ActionModeTitle implements ActionMode.Callback {
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
// Clears the selection
SelectedTrackList.getInstance().clear();
// We are in the music track list
if (mSelectionList.getActivity() instanceof MusicTrackListActivity) {
// Clear the action mode
SelectedTrackList.getInstance().clearActionMode();
updateViews();
// Close the activity
MusicTrackListActivity trackDetailActivity = (MusicTrackListActivity)mSelectionList.getActivity();
trackDetailActivity.finish();
} else {
// Clears the selection
SelectedTrackList.getInstance().clear();
// Update the views
updateViews();
}
}
}

View file

@ -0,0 +1,286 @@
/*
* 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.playmusicexporter2.activities;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import de.arcus.framework.logger.Logger;
import de.arcus.framework.crashhandler.CrashHandler;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.fragments.MusicTrackListFragment;
import de.arcus.playmusicexporter2.fragments.MusicContainerListFragment;
import de.arcus.playmusicexporter2.fragments.NavigationDrawerFragment;
import de.arcus.playmusiclib.PlayMusicManager;
import de.arcus.playmusiclib.datasources.AlbumDataSource;
import de.arcus.playmusiclib.datasources.ArtistDataSource;
import de.arcus.playmusiclib.datasources.PlaylistDataSource;
import de.arcus.playmusiclib.enums.ID3v2Version;
import de.arcus.playmusiclib.items.MusicTrackList;
/**
* An activity representing a list of Tracks. This activity
* has different presentations for handset and tablet-size devices. On
* handsets, the activity presents a list of items, which when touched,
* lead to a {@link MusicTrackListActivity} representing
* item details. On tablets, the activity presents the list of items and
* item details side-by-side using two vertical panes.
* <p/>
* The activity makes heavy use of fragments. The list of items is a
* {@link MusicContainerListFragment} and the item details
* (if present) is a {@link MusicTrackListFragment}.
* <p/>
* This activity also implements the required
* {@link MusicContainerListFragment.Callbacks} interface
* to listen for item selections.
*/
public class MusicContainerListActivity extends AppCompatActivity
implements MusicContainerListFragment.Callbacks
, NavigationDrawerFragment.NavigationDrawerCallbacks,
SearchView.OnQueryTextListener {
@Override
public void onViewTypeChanged(NavigationDrawerFragment.ViewType viewType) {
mViewType = viewType;
loadList();
}
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
private PlayMusicManager mPlayMusicManager;
private NavigationDrawerFragment mNavigationDrawerFragment;
private String mSearchKeyword;
private NavigationDrawerFragment.ViewType mViewType;
private SearchView mSearchView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_track_list);
// Adds the crash handler to this class
CrashHandler.addCrashHandler(this);
Logger.getInstance().logVerbose("Activity", "onCreate(" + this.getLocalClassName() + ")");
// 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);
}
// Gets the running instance
mPlayMusicManager = PlayMusicManager.getInstance();
// Create a new instance
if (mPlayMusicManager == null) {
mPlayMusicManager = new PlayMusicManager(this);
try {
// Simple play ground
mPlayMusicManager.startUp();
mPlayMusicManager.setOfflineOnly(true);
// Setup ID3
mPlayMusicManager.setID3Enable(true);
mPlayMusicManager.setID3EnableArtwork(true);
mPlayMusicManager.setID3EnableFallback(true);
mPlayMusicManager.setID3v2Version(ID3v2Version.ID3v23);
mPlayMusicManager.setID3ArtworkFormat(Bitmap.CompressFormat.JPEG);
mPlayMusicManager.setID3ArtworkMaximumSize(512);
} catch (Exception e) {
Logger.getInstance().logError("SetupPlayMusicExporter", e.toString());
}
}
// Loads the list
mViewType = mNavigationDrawerFragment.getViewType();
loadList();
}
/**
* Update all view lists
*/
public void updateLists() {
Fragment fragmentList = getSupportFragmentManager()
.findFragmentById(R.id.fragment_main);
// Album view
if (fragmentList instanceof MusicContainerListFragment) {
// Gets the music list fragment
MusicContainerListFragment musicTrackListFragment = (MusicContainerListFragment) fragmentList;
// Update the view
musicTrackListFragment.updateListView();
}
}
/**
* Loads the music list with the current view type
*/
private void loadList() {
// Manager is not loaded
if (mPlayMusicManager == null) return;
// Gets the music list fragment
MusicContainerListFragment musicTrackListFragment = (MusicContainerListFragment) getSupportFragmentManager()
.findFragmentById(R.id.fragment_main);
switch(mViewType) {
case Album:
// Load all albums to the list
AlbumDataSource dataSourceAlbum = new AlbumDataSource(mPlayMusicManager);
dataSourceAlbum.setOfflineOnly(true);
dataSourceAlbum.setSerchKey(mSearchKeyword);
musicTrackListFragment.setMusicTrackList(dataSourceAlbum.getAll());
break;
case Artist:
// Load all artists to the list
ArtistDataSource dataSourceArtist = new ArtistDataSource(mPlayMusicManager);
dataSourceArtist.setOfflineOnly(true);
dataSourceArtist.setSerchKey(mSearchKeyword);
musicTrackListFragment.setMusicTrackList(dataSourceArtist.getAll());
break;
case Playlist:
// Load all playlists to the list
PlaylistDataSource dataSourcePlaylist = new PlaylistDataSource(mPlayMusicManager);
dataSourcePlaylist.setOfflineOnly(true);
dataSourcePlaylist.setSerchKey(mSearchKeyword);
musicTrackListFragment.setMusicTrackList(dataSourcePlaylist.getAll());
break;
case Rated:
// Load all reated albums to the list
AlbumDataSource dataSourceRatedAlbum = new AlbumDataSource(mPlayMusicManager);
dataSourceRatedAlbum.setOfflineOnly(true);
dataSourceRatedAlbum.setRatedOnly(true);
dataSourceRatedAlbum.setSerchKey(mSearchKeyword);
musicTrackListFragment.setMusicTrackList(dataSourceRatedAlbum.getAll());
break;
}
}
/**
* Callback method from {@link MusicContainerListFragment.Callbacks}
* indicating that the item with the given ID was selected.
*/
@Override
public void onItemSelected(MusicTrackList musicTrackList) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
arguments.putLong(MusicTrackListFragment.ARG_MUSIC_TRACK_LIST_ID, musicTrackList.getMusicTrackListID());
arguments.putString(MusicTrackListFragment.ARG_MUSIC_TRACK_LIST_TYPE, musicTrackList.getMusicTrackListType());
MusicTrackListFragment fragment = new MusicTrackListFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.track_detail_container, fragment)
.commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, MusicTrackListActivity.class);
detailIntent.putExtra(MusicTrackListFragment.ARG_MUSIC_TRACK_LIST_ID, musicTrackList.getMusicTrackListID());
detailIntent.putExtra(MusicTrackListFragment.ARG_MUSIC_TRACK_LIST_TYPE, musicTrackList.getMusicTrackListType());
startActivity(detailIntent);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.music_track_list, menu);
// Finds the search item and create the search view
MenuItem itemSearch = menu.findItem(R.id.action_search);
mSearchView = (SearchView)MenuItemCompat.getActionView(itemSearch);
if (mSearchView != null) {
// Sets the search listener
mSearchView.setOnQueryTextListener(this);
mSearchView.setIconifiedByDefault(false);
}
return true;
}
@Override
public boolean onQueryTextSubmit(String keyword) {
mSearchView.clearFocus();
return false;
}
@Override
public boolean onQueryTextChange(String keyword) {
mSearchKeyword = keyword;
loadList();
return false;
}
}

View file

@ -1,128 +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.playmusicexporter2.activities;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.NavUtils;
import android.view.MenuItem;
import de.arcus.framework.crashhandler.CrashHandler;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.fragments.MusicTrackDetailFragment;
import de.arcus.playmusiclib.PlayMusicManager;
import de.arcus.playmusiclib.items.MusicTrackList;
/**
* An activity representing a single Track detail screen. This
* activity is only used on handset devices. On tablet-size devices,
* item details are presented side-by-side with a list of items
* in a {@link MusicTrackListActivity}.
* <p/>
* This activity is mostly just a 'shell' activity containing nothing
* more than a {@link de.arcus.playmusicexporter2.fragments.MusicTrackDetailFragment}.
*/
public class MusicTrackDetailActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_track_detail);
// Adds the crash handler to this class
CrashHandler.addCrashHandler(this);
// Show the Up button in the action bar.
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// savedInstanceState is non-null when there is fragment state
// saved from previous configurations of this activity
// (e.g. when rotating the screen from portrait to landscape).
// In this case, the fragment will automatically be re-added
// to its container so we don't need to manually add it.
// For more information, see the Fragments API guide at:
//
// http://developer.android.com/guide/components/fragments.html
//
if (savedInstanceState == null) {
// Create the detail fragment and add it to the activity
// using a fragment transaction.
Bundle arguments = new Bundle();
// Loads the track list
long id = getIntent().getLongExtra(MusicTrackDetailFragment.ARG_MUSIC_TRACK_LIST_ID, 0);
String type = getIntent().getStringExtra(MusicTrackDetailFragment.ARG_MUSIC_TRACK_LIST_TYPE);
PlayMusicManager playMusicManager = PlayMusicManager.getInstance();
if (playMusicManager != null) {
MusicTrackList musicTrackList = MusicTrackList.deserialize(playMusicManager, id, type);
// Sets the title
setTitle(musicTrackList.getTitle());
}
// Puts the track list information to the fragment
arguments.putLong(MusicTrackDetailFragment.ARG_MUSIC_TRACK_LIST_ID, id);
arguments.putString(MusicTrackDetailFragment.ARG_MUSIC_TRACK_LIST_TYPE, type);
// Loads the fragment
MusicTrackDetailFragment fragment = new MusicTrackDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.add(R.id.track_detail_container, fragment)
.commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpTo(this, new Intent(this, MusicTrackListActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Update all view lists
*/
public void updateLists() {
// Gets the music list fragment
MusicTrackDetailFragment musicTrackDetailFragment = (MusicTrackDetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.track_detail_container);
musicTrackDetailFragment.updateListView();
}
}

View file

@ -24,138 +24,101 @@ package de.arcus.playmusicexporter2.activities;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuInflater;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import de.arcus.framework.logger.Logger;
import de.arcus.framework.crashhandler.CrashHandler;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.fragments.MusicTrackDetailFragment;
import de.arcus.playmusicexporter2.fragments.MusicTrackListFragment;
import de.arcus.playmusicexporter2.fragments.NavigationDrawerFragment;
import de.arcus.playmusiclib.PlayMusicManager;
import de.arcus.playmusiclib.datasources.AlbumDataSource;
import de.arcus.playmusiclib.datasources.ArtistDataSource;
import de.arcus.playmusiclib.datasources.PlaylistDataSource;
import de.arcus.playmusiclib.enums.ID3v2Version;
import de.arcus.playmusiclib.items.MusicTrackList;
/**
* An activity representing a list of Tracks. This activity
* has different presentations for handset and tablet-size devices. On
* handsets, the activity presents a list of items, which when touched,
* lead to a {@link MusicTrackDetailActivity} representing
* item details. On tablets, the activity presents the list of items and
* item details side-by-side using two vertical panes.
* An activity representing a single Track detail screen. This
* activity is only used on handset devices. On tablet-size devices,
* item details are presented side-by-side with a list of items
* in a {@link MusicContainerListActivity}.
* <p/>
* The activity makes heavy use of fragments. The list of items is a
* {@link de.arcus.playmusicexporter2.fragments.MusicTrackListFragment} and the item details
* (if present) is a {@link de.arcus.playmusicexporter2.fragments.MusicTrackDetailFragment}.
* <p/>
* This activity also implements the required
* {@link de.arcus.playmusicexporter2.fragments.MusicTrackListFragment.Callbacks} interface
* to listen for item selections.
* This activity is mostly just a 'shell' activity containing nothing
* more than a {@link MusicTrackListFragment}.
*/
public class MusicTrackListActivity extends ActionBarActivity
implements MusicTrackListFragment.Callbacks
, NavigationDrawerFragment.NavigationDrawerCallbacks,
SearchView.OnQueryTextListener {
@Override
public void onViewTypeChanged(NavigationDrawerFragment.ViewType viewType) {
mViewType = viewType;
loadList();
}
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
private PlayMusicManager mPlayMusicManager;
private NavigationDrawerFragment mNavigationDrawerFragment;
private String mSearchKeyword;
private NavigationDrawerFragment.ViewType mViewType;
private SearchView mSearchView;
public class MusicTrackListActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_track_list);
setContentView(R.layout.activity_track_detail);
// Adds the crash handler to this class
CrashHandler.addCrashHandler(this);
Logger.getInstance().logVerbose("Activity", "onCreate(" + this.getLocalClassName() + ")");
// Setup ActionBar
// Show the Up button in the action bar.
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(R.string.app_name);
actionBar.setDisplayHomeAsUpEnabled(true);
}
// savedInstanceState is non-null when there is fragment state
// saved from previous configurations of this activity
// (e.g. when rotating the screen from portrait to landscape).
// In this case, the fragment will automatically be re-added
// to its container so we don't need to manually add it.
// For more information, see the Fragments API guide at:
//
// http://developer.android.com/guide/components/fragments.html
//
if (savedInstanceState == null) {
// Create the detail fragment and add it to the activity
// using a fragment transaction.
Bundle arguments = new Bundle();
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
// Loads the track list
long id = getIntent().getLongExtra(MusicTrackListFragment.ARG_MUSIC_TRACK_LIST_ID, 0);
String type = getIntent().getStringExtra(MusicTrackListFragment.ARG_MUSIC_TRACK_LIST_TYPE);
mNavigationDrawerFragment.setOnListViewChanged(this);
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
PlayMusicManager playMusicManager = PlayMusicManager.getInstance();
if (playMusicManager != null) {
MusicTrackList musicTrackList = MusicTrackList.deserialize(playMusicManager, id, type);
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.
((MusicTrackListFragment) getSupportFragmentManager()
.findFragmentById(R.id.track_list))
.setActivateOnItemClick(true);
}
// Gets the running instance
mPlayMusicManager = PlayMusicManager.getInstance();
// Create a new instance
if (mPlayMusicManager == null) {
mPlayMusicManager = new PlayMusicManager(this);
try {
// Simple play ground
mPlayMusicManager.startUp();
mPlayMusicManager.setOfflineOnly(true);
// Setup ID3
mPlayMusicManager.setID3Enable(true);
mPlayMusicManager.setID3EnableArtwork(true);
mPlayMusicManager.setID3EnableFallback(true);
mPlayMusicManager.setID3v2Version(ID3v2Version.ID3v23);
} catch (Exception e) {
Logger.getInstance().logError("Test", e.toString());
if (musicTrackList != null) {
// Sets the title
setTitle(musicTrackList.getTitle());
}
}
}
// Loads the list
mViewType = mNavigationDrawerFragment.getViewType();
loadList();
// Puts the track list information to the fragment
arguments.putLong(MusicTrackListFragment.ARG_MUSIC_TRACK_LIST_ID, id);
arguments.putString(MusicTrackListFragment.ARG_MUSIC_TRACK_LIST_TYPE, type);
// Loads the fragment
MusicTrackListFragment fragment = new MusicTrackListFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.add(R.id.track_detail_container, fragment)
.commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpTo(this, new Intent(this, MusicContainerListActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
/**
@ -163,120 +126,9 @@ public class MusicTrackListActivity extends ActionBarActivity
*/
public void updateLists() {
// Gets the music list fragment
MusicTrackListFragment musicTrackListFragment = (MusicTrackListFragment) getSupportFragmentManager()
.findFragmentById(R.id.track_list);
musicTrackListFragment.updateListView();
// Gets the music list fragment
MusicTrackDetailFragment musicTrackDetailFragment = (MusicTrackDetailFragment) getSupportFragmentManager()
MusicTrackListFragment musicTrackDetailFragment = (MusicTrackListFragment) getSupportFragmentManager()
.findFragmentById(R.id.track_detail_container);
musicTrackDetailFragment.updateListView();
}
/**
* Loads the music list with the current view type
*/
private void loadList() {
// Manager is not loaded
if (mPlayMusicManager == null) return;
// Gets the music list fragment
MusicTrackListFragment musicTrackListFragment = (MusicTrackListFragment) getSupportFragmentManager()
.findFragmentById(R.id.track_list);
switch(mViewType) {
case Album:
// Load all albums to the list
AlbumDataSource dataSourceAlbum = new AlbumDataSource(mPlayMusicManager);
dataSourceAlbum.setOfflineOnly(true);
dataSourceAlbum.setSerchKey(mSearchKeyword);
musicTrackListFragment.setMusicTrackList(dataSourceAlbum.getAll());
break;
case Artist:
// Load all artists to the list
ArtistDataSource dataSourceArtist = new ArtistDataSource(mPlayMusicManager);
dataSourceArtist.setOfflineOnly(true);
dataSourceArtist.setSerchKey(mSearchKeyword);
musicTrackListFragment.setMusicTrackList(dataSourceArtist.getAll());
break;
case Playlist:
// Load all playlists to the list
PlaylistDataSource dataSourcePlaylist = new PlaylistDataSource(mPlayMusicManager);
dataSourcePlaylist.setOfflineOnly(true);
dataSourcePlaylist.setSerchKey(mSearchKeyword);
musicTrackListFragment.setMusicTrackList(dataSourcePlaylist.getAll());
break;
case Rated:
// Load all reated albums to the list
AlbumDataSource dataSourceRatedAlbum = new AlbumDataSource(mPlayMusicManager);
dataSourceRatedAlbum.setOfflineOnly(true);
dataSourceRatedAlbum.setRatedOnly(true);
dataSourceRatedAlbum.setSerchKey(mSearchKeyword);
musicTrackListFragment.setMusicTrackList(dataSourceRatedAlbum.getAll());
break;
}
}
/**
* Callback method from {@link de.arcus.playmusicexporter2.fragments.MusicTrackListFragment.Callbacks}
* indicating that the item with the given ID was selected.
*/
@Override
public void onItemSelected(MusicTrackList musicTrackList) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
arguments.putLong(MusicTrackDetailFragment.ARG_MUSIC_TRACK_LIST_ID, musicTrackList.getMusicTrackListID());
arguments.putString(MusicTrackDetailFragment.ARG_MUSIC_TRACK_LIST_TYPE, musicTrackList.getMusicTrackListType());
MusicTrackDetailFragment fragment = new MusicTrackDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.track_detail_container, fragment)
.commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, MusicTrackDetailActivity.class);
detailIntent.putExtra(MusicTrackDetailFragment.ARG_MUSIC_TRACK_LIST_ID, musicTrackList.getMusicTrackListID());
detailIntent.putExtra(MusicTrackDetailFragment.ARG_MUSIC_TRACK_LIST_TYPE, musicTrackList.getMusicTrackListType());
startActivity(detailIntent);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.music_track_list, menu);
// Finds the search item and create the search view
MenuItem itemSearch = menu.findItem(R.id.action_search);
mSearchView = (SearchView)MenuItemCompat.getActionView(itemSearch);
if (mSearchView != null) {
// Sets the search listener
mSearchView.setOnQueryTextListener(this);
mSearchView.setIconifiedByDefault(false);
}
return true;
}
@Override
public boolean onQueryTextSubmit(String keyword) {
mSearchView.clearFocus();
return false;
}
@Override
public boolean onQueryTextChange(String keyword) {
mSearchKeyword = keyword;
loadList();
return false;
}
}

View file

@ -0,0 +1,102 @@
/*
* 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.playmusicexporter2.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.utils.ArtworkViewLoader;
import de.arcus.playmusiclib.items.MusicTrackList;
/**
* Adapter for the music track lists
*/
public class MusicContainerListAdapter extends ArrayAdapter<MusicTrackList> {
/**
* The context of the app
*/
private Context mContext;
/**
* Create a new track list adapter
* @param context The app context
*/
public MusicContainerListAdapter(Context context) {
super(context, R.layout.adapter_music_track);
mContext = context;
}
public void setList(List<MusicTrackList> musicTrackLists) {
// Clear all items
clear();
// Add the new items
for(MusicTrackList musicTrackList : musicTrackLists) {
add(musicTrackList);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// The track list
MusicTrackList musicTrackList = getItem(position);
View view = convertView;
// Inflates a view
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.adapter_music_track_list, parent, false);
}
TextView textView;
ImageView imageView;
// Set the title
textView = (TextView)view.findViewById(R.id.text_music_track_list_title);
textView.setText(musicTrackList.getTitle());
// Set the description
textView = (TextView)view.findViewById(R.id.text_music_track_list_description);
textView.setText(musicTrackList.getDescription());
// Final for the callback
imageView = (ImageView)view.findViewById(R.id.image_music_track_artwork);
// Gets the artwork
String artworkPath = musicTrackList.getArtworkPath();
String artworkLocation = musicTrackList.getArtworkLocation();
// Loads the artwork
ArtworkViewLoader.loadImage(imageView, artworkPath, artworkLocation, R.drawable.cd_case);
return view;
}
}

View file

@ -1,162 +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.playmusicexporter2.adapter;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.items.SelectedTrack;
import de.arcus.playmusicexporter2.items.SelectedTrackList;
import de.arcus.playmusicexporter2.utils.ImageViewLoader;
import de.arcus.playmusiclib.items.MusicTrack;
/**
* Adapter for the music tracks
*/
public class MusicTrackAdapter extends ArrayAdapter<MusicTrack> {
/**
* The context of the app
*/
private Context mContext;
/**
* If this is set the music track shows it artwork instead of the track number.
* Used for playlists.
*/
private boolean mShowArtworks = true;
public boolean getShowArtwok() {
return mShowArtworks;
}
public void setShowArtworks(boolean showArtworks) {
mShowArtworks = showArtworks;
}
/**
* Create a new track adapter
* @param context The app context
*/
public MusicTrackAdapter(Context context) {
super(context, R.layout.adapter_music_track);
mContext = context;
}
public void setList(List<MusicTrack> musicTracks) {
// Clear all items
clear();
// Add the new items
for(MusicTrack musicTrack : musicTracks) {
add(musicTrack);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// The track
MusicTrack musicTrack = getItem(position);
View view = convertView;
// Inflates a view
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.adapter_music_track, parent, false);
}
TextView textView;
// Sets the track number
textView = (TextView)view.findViewById(R.id.text_music_track_number);
long trackPosition = musicTrack.getTrackNumber();
if (musicTrack.getContainerName() != null)
trackPosition = musicTrack.getContainerPosition();
if (trackPosition > 0)
textView.setText("" + trackPosition);
else
textView.setText("");
textView.setTextColor(mContext.getResources().getColor(musicTrack.isOfflineAvailable() ? R.color.text_music_number : R.color.text_music_disable_number));
// Sets the disc number
textView = (TextView)view.findViewById(R.id.text_music_track_disc_number);
textView.setText("CD " + musicTrack.getDiscNumber());
// Don't show the disc number if this is a playlist or artist list
if (musicTrack.getDiscNumber() > 0 && TextUtils.isEmpty(musicTrack.getContainerName()))
textView.setVisibility(View.VISIBLE);
else
textView.setVisibility(View.GONE);
textView.setTextColor(mContext.getResources().getColor(musicTrack.isOfflineAvailable() ? R.color.text_music_disc_number : R.color.text_music_disable_disc_number));
if (mShowArtworks) {
view.findViewById(R.id.relative_layout_number).setVisibility(View.GONE);
view.findViewById(R.id.relative_layout_artwork).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.relative_layout_number).setVisibility(View.VISIBLE);
view.findViewById(R.id.relative_layout_artwork).setVisibility(View.GONE);
}
// Shows the artwork
if (mShowArtworks) {
ImageView imageView = (ImageView) view.findViewById(R.id.image_music_track_artwork);
String artworkPath = musicTrack.getArtworkPath();
ImageViewLoader.loadImage(imageView, artworkPath, R.drawable.cd_case);
}
// Sets the title
textView = (TextView)view.findViewById(R.id.text_music_track_title);
textView.setText(musicTrack.getTitle());
textView.setTextColor(mContext.getResources().getColor(musicTrack.isOfflineAvailable() ? R.color.text_music_title : R.color.text_music_disable_title));
// Sets the artist
textView = (TextView)view.findViewById(R.id.text_music_track_artist);
textView.setText(musicTrack.getArtist());
textView.setTextColor(mContext.getResources().getColor(musicTrack.isOfflineAvailable() ? R.color.text_music_description : R.color.text_music_disable_description));
// Track is available?
view.setEnabled(musicTrack.isOfflineAvailable());
// Selected state
SelectedTrackList.getInstance().initView(new SelectedTrack(musicTrack.getId()), view);
return view;
}
}

View file

@ -23,6 +23,7 @@
package de.arcus.playmusicexporter2.adapter;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -33,20 +34,36 @@ import android.widget.TextView;
import java.util.List;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.utils.ImageViewLoader;
import de.arcus.playmusiclib.items.MusicTrackList;
import de.arcus.playmusicexporter2.items.SelectedTrack;
import de.arcus.playmusicexporter2.items.SelectedTrackList;
import de.arcus.playmusicexporter2.utils.ArtworkViewLoader;
import de.arcus.playmusiclib.items.MusicTrack;
/**
* Adapter for the music track lists
* Adapter for the music tracks
*/
public class MusicTrackListAdapter extends ArrayAdapter<MusicTrackList> {
public class MusicTrackListAdapter extends ArrayAdapter<MusicTrack> {
/**
* The context of the app
*/
private Context mContext;
/**
* Create a new track list adapter
* If this is set the music track shows it artwork instead of the track number.
* Used for playlists.
*/
private boolean mShowArtworks = true;
public boolean getShowArtwok() {
return mShowArtworks;
}
public void setShowArtworks(boolean showArtworks) {
mShowArtworks = showArtworks;
}
/**
* Create a new track adapter
* @param context The app context
*/
public MusicTrackListAdapter(Context context) {
@ -54,48 +71,93 @@ public class MusicTrackListAdapter extends ArrayAdapter<MusicTrackList> {
mContext = context;
}
public void setList(List<MusicTrackList> musicTrackLists) {
public void setList(List<MusicTrack> musicTracks) {
// Clear all items
clear();
// Add the new items
for(MusicTrackList musicTrackList : musicTrackLists) {
add(musicTrackList);
for(MusicTrack musicTrack : musicTracks) {
add(musicTrack);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// The track list
MusicTrackList musicTrackList = getItem(position);
// The track
MusicTrack musicTrack = getItem(position);
View view = convertView;
// Inflates a view
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.adapter_music_track_list, parent, false);
view = inflater.inflate(R.layout.adapter_music_track, parent, false);
}
TextView textView;
ImageView imageView;
// Set the title
textView = (TextView)view.findViewById(R.id.text_music_track_list_title);
textView.setText(musicTrackList.getTitle());
// Sets the track number
textView = (TextView)view.findViewById(R.id.text_music_track_number);
long trackPosition = musicTrack.getTrackNumber();
// Set the description
textView = (TextView)view.findViewById(R.id.text_music_track_list_description);
textView.setText(musicTrackList.getDescription());
if (musicTrack.getContainerName() != null)
trackPosition = musicTrack.getContainerPosition();
// Final for the callback
imageView = (ImageView)view.findViewById(R.id.image_music_track_artwork);
if (trackPosition > 0)
textView.setText("" + trackPosition);
else
textView.setText("");
// Gets the artwork
String artworkPath = musicTrackList.getArtworkPath();
textView.setTextColor(mContext.getResources().getColor(musicTrack.isOfflineAvailable() ? R.color.text_music_number : R.color.text_music_disable_number));
// Loads the artwork
ImageViewLoader.loadImage(imageView, artworkPath, R.drawable.cd_case);
// Sets the disc number
textView = (TextView)view.findViewById(R.id.text_music_track_disc_number);
textView.setText("CD " + musicTrack.getDiscNumber());
// Don't show the disc number if this is a playlist or artist list
if (musicTrack.getDiscNumber() > 0 && TextUtils.isEmpty(musicTrack.getContainerName()))
textView.setVisibility(View.VISIBLE);
else
textView.setVisibility(View.GONE);
textView.setTextColor(mContext.getResources().getColor(musicTrack.isOfflineAvailable() ? R.color.text_music_disc_number : R.color.text_music_disable_disc_number));
if (mShowArtworks) {
view.findViewById(R.id.relative_layout_number).setVisibility(View.GONE);
view.findViewById(R.id.relative_layout_artwork).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.relative_layout_number).setVisibility(View.VISIBLE);
view.findViewById(R.id.relative_layout_artwork).setVisibility(View.GONE);
}
// Shows the artwork
if (mShowArtworks) {
ImageView imageView = (ImageView) view.findViewById(R.id.image_music_track_artwork);
String artworkPath = musicTrack.getArtworkPath();
String artworkLocation = musicTrack.getArtworkLocation();
ArtworkViewLoader.loadImage(imageView, artworkPath, artworkLocation, R.drawable.cd_case);
}
// Sets the title
textView = (TextView)view.findViewById(R.id.text_music_track_title);
textView.setText(musicTrack.getTitle());
textView.setTextColor(mContext.getResources().getColor(musicTrack.isOfflineAvailable() ? R.color.text_music_title : R.color.text_music_disable_title));
// Sets the artist
textView = (TextView)view.findViewById(R.id.text_music_track_artist);
textView.setText(musicTrack.getArtist());
textView.setTextColor(mContext.getResources().getColor(musicTrack.isOfflineAvailable() ? R.color.text_music_description : R.color.text_music_disable_description));
// Track is available?
view.setEnabled(musicTrack.isOfflineAvailable());
// Selected state
SelectedTrackList.getInstance().initView(new SelectedTrack(musicTrack.getId()), view);
return view;
}
}

View file

@ -0,0 +1,207 @@
/*
* 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.playmusicexporter2.fragments;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.View;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import de.arcus.playmusicexporter2.adapter.MusicContainerListAdapter;
import de.arcus.playmusiclib.items.MusicTrackList;
/**
* A list fragment representing a list of Tracks. This fragment
* also supports tablet devices by allowing list items to be given an
* 'activated' state upon selection. This helps indicate which item is
* currently being viewed in a {@link MusicTrackListFragment}.
* <p/>
* Activities containing this fragment MUST implement the {@link Callbacks}
* interface.
*/
public class MusicContainerListFragment extends ListFragment {
/**
* The serialization (saved instance state) Bundle key representing the
* activated item position. Only used on tablets.
*/
private static final String STATE_ACTIVATED_POSITION = "activated_position";
/**
* The fragment's current callback object, which is notified of list item
* clicks.
*/
private Callbacks mCallbacks = sDummyCallbacks;
/**
* The current activated item position. Only used on tablets.
*/
private int mActivatedPosition = ListView.INVALID_POSITION;
/**
* A callback interface that all activities containing this fragment must
* implement. This mechanism allows activities to be notified of item
* selections.
*/
public interface Callbacks {
/**
* Callback for when an item has been selected.
*/
void onItemSelected(MusicTrackList musicTrackList);
}
/**
* 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 MusicContainerListAdapter mMusicTrackListAdapter;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public MusicContainerListFragment() {
}
/**
* Update the list view
*/
public void updateListView() {
getListView().invalidateViews();
}
/**
* @param list Set the list
*/
public void setMusicTrackList(List<? extends MusicTrackList> list) {
// Create a new list
List<MusicTrackList> newList = new ArrayList<>();
// Null check
if (list != null) {
// Copy the list
for (MusicTrackList musicTrackList : list) {
newList.add(musicTrackList);
}
}
// Set the list in the adapter
mMusicTrackListAdapter.setList(newList);
getListView().invalidateViews();
getListView().setSelection(0);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMusicTrackListAdapter = new MusicContainerListAdapter(getActivity());
setListAdapter(mMusicTrackListAdapter);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Restore the previously serialized activated item position.
if (savedInstanceState != null
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Activities containing this fragment must implement its callbacks.
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
mCallbacks = (Callbacks) activity;
}
@Override
public void onDetach() {
super.onDetach();
// Reset the active callbacks interface to the dummy implementation.
mCallbacks = sDummyCallbacks;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mCallbacks.onItemSelected(mMusicTrackListAdapter.getItem(position));
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivatedPosition != ListView.INVALID_POSITION) {
// Serialize and persist the activated item position.
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
}
/**
* Turns on activate-on-click mode. When this mode is on, list items will be
* given the 'activated' state when touched.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
// When setting CHOICE_MODE_SINGLE, ListView will automatically
// give items the 'activated' state when touched.
getListView().setChoiceMode(activateOnItemClick
? ListView.CHOICE_MODE_SINGLE
: ListView.CHOICE_MODE_NONE);
}
private void setActivatedPosition(int position) {
if (position == ListView.INVALID_POSITION) {
getListView().setItemChecked(mActivatedPosition, false);
} else {
getListView().setItemChecked(position, true);
}
mActivatedPosition = position;
}
}

View file

@ -1,193 +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.playmusicexporter2.fragments;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
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;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.adapter.MusicTrackAdapter;
import de.arcus.playmusicexporter2.items.SelectedTrack;
import de.arcus.playmusicexporter2.items.SelectedTrackList;
import de.arcus.playmusicexporter2.utils.ImageViewLoader;
import de.arcus.playmusicexporter2.utils.MusicPathBuilder;
import de.arcus.playmusiclib.PlayMusicManager;
import de.arcus.playmusiclib.items.MusicTrack;
import de.arcus.playmusiclib.items.MusicTrackList;
/**
* A fragment representing a single Track detail screen.
* This fragment is either contained in a {@link de.arcus.playmusicexporter2.activities.MusicTrackListActivity}
* in two-pane mode (on tablets) or a {@link de.arcus.playmusicexporter2.activities.MusicTrackDetailActivity}
* on handsets.
*/
public class MusicTrackDetailFragment extends Fragment {
/**
* The fragment argument representing the item ID that this fragment
* represents.
*/
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";
/**
* The track list
*/
private MusicTrackList mMusicTrackList;
/**
* The list view
*/
private ListView mListView;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public MusicTrackDetailFragment() {
}
/**
* Update the list view
*/
public void updateListView() {
mListView.invalidateViews();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments().containsKey(ARG_MUSIC_TRACK_LIST_ID)
&& getArguments().containsKey(ARG_MUSIC_TRACK_LIST_TYPE)) {
// Loads the track list
long id = getArguments().getLong(ARG_MUSIC_TRACK_LIST_ID);
String type = getArguments().getString(ARG_MUSIC_TRACK_LIST_TYPE);
PlayMusicManager playMusicManager = PlayMusicManager.getInstance();
if (playMusicManager != null) {
mMusicTrackList = MusicTrackList.deserialize(playMusicManager, id, type);
}
}
// Setup the selection list for this activity
SelectedTrackList.getInstance().setupActionMode((ActionBarActivity)getActivity());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_track_detail, container, false);
// Show the dummy content as text in a TextView.
if (mMusicTrackList != null) {
mListView = (ListView)rootView.findViewById(R.id.list_music_track);
final MusicTrackAdapter musicTrackAdapter = new MusicTrackAdapter(getActivity());
musicTrackAdapter.setShowArtworks(mMusicTrackList.getShowArtworkInTrack());
View headerView = inflater.inflate(R.layout.header_music_track_list, mListView, false);
headerView.setEnabled(false);
TextView textView;
ImageView imageView;
// Sets the artwork image
imageView = (ImageView)headerView.findViewById(R.id.image_music_track_artwork);
String artworkPath = mMusicTrackList.getArtworkPath();
// Loads the artwork
ImageViewLoader.loadImage(imageView, artworkPath, R.drawable.cd_case);
// Sets the 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.setText(mMusicTrackList.getDescription());
mListView.addHeaderView(headerView);
musicTrackAdapter.setList(mMusicTrackList.getMusicTrackList());
mListView.setAdapter(musicTrackAdapter);
//listView.setDrawSelectorOnTop(false);
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mListView.setItemsCanFocus(false);
// 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;
// Gets the selected track
MusicTrack musicTrack = musicTrackAdapter.getItem(position);
// Track is available
if (musicTrack.isOfflineAvailable()) {
// Default structure
String pathStructure = "{album-artist}/{album}/{disc=CD $}/{no=$$.} {title}.mp3";
// Track is exported from a group (playlist or artist)
if (!TextUtils.isEmpty(musicTrack.getContainerName()))
{
pathStructure = "{group}/{group-no=$$.} {title}.mp3";
}
// Build the path
String path = MusicPathBuilder.Build(musicTrack, pathStructure);
// Path to the public music folder
path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC) + "/" + path;
// Toggles the music track
SelectedTrackList.getInstance().toggle(new SelectedTrack(musicTrack.getId(), path), view);
}
}
}
});
}
return rootView;
}
}

View file

@ -22,70 +22,55 @@
package de.arcus.playmusicexporter2.fragments;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.os.Environment;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
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;
import java.util.ArrayList;
import java.util.List;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusicexporter2.activities.MusicContainerListActivity;
import de.arcus.playmusicexporter2.activities.MusicTrackListActivity;
import de.arcus.playmusicexporter2.adapter.MusicTrackListAdapter;
import de.arcus.playmusicexporter2.items.SelectedTrack;
import de.arcus.playmusicexporter2.items.SelectedTrackList;
import de.arcus.playmusicexporter2.utils.ArtworkViewLoader;
import de.arcus.playmusicexporter2.utils.MusicPathBuilder;
import de.arcus.playmusiclib.PlayMusicManager;
import de.arcus.playmusiclib.items.MusicTrack;
import de.arcus.playmusiclib.items.MusicTrackList;
/**
* A list fragment representing a list of Tracks. This fragment
* also supports tablet devices by allowing list items to be given an
* 'activated' state upon selection. This helps indicate which item is
* currently being viewed in a {@link MusicTrackDetailFragment}.
* <p/>
* Activities containing this fragment MUST implement the {@link Callbacks}
* interface.
* A fragment representing a single Track detail screen.
* This fragment is either contained in a {@link MusicContainerListActivity}
* in two-pane mode (on tablets) or a {@link MusicTrackListActivity}
* on handsets.
*/
public class MusicTrackListFragment extends ListFragment {
public class MusicTrackListFragment extends Fragment {
/**
* The fragment argument representing the item ID that this fragment
* represents.
*/
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";
/**
* The serialization (saved instance state) Bundle key representing the
* activated item position. Only used on tablets.
* The track list
*/
private static final String STATE_ACTIVATED_POSITION = "activated_position";
private MusicTrackList mMusicTrackList;
/**
* The fragment's current callback object, which is notified of list item
* clicks.
* The list view
*/
private Callbacks mCallbacks = sDummyCallbacks;
/**
* The current activated item position. Only used on tablets.
*/
private int mActivatedPosition = ListView.INVALID_POSITION;
/**
* A callback interface that all activities containing this fragment must
* implement. This mechanism allows activities to be notified of item
* selections.
*/
public interface Callbacks {
/**
* Callback for when an item has been selected.
*/
void onItemSelected(MusicTrackList musicTrackList);
}
/**
* 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 MusicTrackListAdapter mMusicTrackListAdapter;
private ListView mListView;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
@ -98,106 +83,115 @@ public class MusicTrackListFragment extends ListFragment {
* Update the list view
*/
public void updateListView() {
getListView().invalidateViews();
if (mListView != null)
mListView.invalidateViews();
}
/**
* @param list Set the list
*/
public void setMusicTrackList(List<? extends MusicTrackList> list) {
// Create a new list
List<MusicTrackList> newList = new ArrayList<>();
// Copy the list
for(MusicTrackList musicTrackList : list) {
newList.add(musicTrackList);
}
// Set the list in the adapter
mMusicTrackListAdapter.setList(newList);
getListView().invalidateViews();
getListView().setSelection(0);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMusicTrackListAdapter = new MusicTrackListAdapter(getActivity());
if (getArguments().containsKey(ARG_MUSIC_TRACK_LIST_ID)
&& getArguments().containsKey(ARG_MUSIC_TRACK_LIST_TYPE)) {
// Loads the track list
long id = getArguments().getLong(ARG_MUSIC_TRACK_LIST_ID);
String type = getArguments().getString(ARG_MUSIC_TRACK_LIST_TYPE);
setListAdapter(mMusicTrackListAdapter);
}
PlayMusicManager playMusicManager = PlayMusicManager.getInstance();
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Restore the previously serialized activated item position.
if (savedInstanceState != null
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Activities containing this fragment must implement its callbacks.
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
if (playMusicManager != null) {
mMusicTrackList = MusicTrackList.deserialize(playMusicManager, id, type);
}
}
mCallbacks = (Callbacks) activity;
// Setup the selection list for this activity
SelectedTrackList.getInstance().setupActionMode((AppCompatActivity)getActivity());
}
@Override
public void onDetach() {
super.onDetach();
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_track_detail, container, false);
// Reset the active callbacks interface to the dummy implementation.
mCallbacks = sDummyCallbacks;
}
// Show the dummy content as text in a TextView.
if (mMusicTrackList != null) {
mListView = (ListView)rootView.findViewById(R.id.list_music_track);
final MusicTrackListAdapter musicTrackAdapter = new MusicTrackListAdapter(getActivity());
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
musicTrackAdapter.setShowArtworks(mMusicTrackList.getShowArtworkInTrack());
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mCallbacks.onItemSelected(mMusicTrackListAdapter.getItem(position));
}
View headerView = inflater.inflate(R.layout.header_music_track_list, mListView, false);
headerView.setEnabled(false);
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivatedPosition != ListView.INVALID_POSITION) {
// Serialize and persist the activated item position.
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
}
TextView textView;
ImageView imageView;
/**
* Turns on activate-on-click mode. When this mode is on, list items will be
* given the 'activated' state when touched.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
// When setting CHOICE_MODE_SINGLE, ListView will automatically
// give items the 'activated' state when touched.
getListView().setChoiceMode(activateOnItemClick
? ListView.CHOICE_MODE_SINGLE
: ListView.CHOICE_MODE_NONE);
}
// Sets the artwork image
imageView = (ImageView)headerView.findViewById(R.id.image_music_track_artwork);
private void setActivatedPosition(int position) {
if (position == ListView.INVALID_POSITION) {
getListView().setItemChecked(mActivatedPosition, false);
} else {
getListView().setItemChecked(position, true);
String artworkPath = mMusicTrackList.getArtworkPath();
String artworkLocation = mMusicTrackList.getArtworkLocation();
// Loads the artwork
ArtworkViewLoader.loadImage(imageView, artworkPath, artworkLocation, R.drawable.cd_case);
// Sets the 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.setText(mMusicTrackList.getDescription());
mListView.addHeaderView(headerView);
musicTrackAdapter.setList(mMusicTrackList.getMusicTrackList());
mListView.setAdapter(musicTrackAdapter);
//listView.setDrawSelectorOnTop(false);
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mListView.setItemsCanFocus(false);
// 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;
// Gets the selected track
MusicTrack musicTrack = musicTrackAdapter.getItem(position);
// Track is available
if (musicTrack.isOfflineAvailable()) {
// Default structure
String pathStructure = "{album-artist}/{album}/{disc=CD $}/{no=$$.} {title}.mp3";
// Track is exported from a group (playlist or artist)
if (!TextUtils.isEmpty(musicTrack.getContainerName()))
{
pathStructure = "{group}/{group-no=$$.} {title}.mp3";
}
// Build the path
String path = MusicPathBuilder.Build(musicTrack, pathStructure);
// Path to the public music folder
path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC) + "/" + path;
// Toggles the music track
SelectedTrackList.getInstance().toggle(new SelectedTrack(musicTrack.getId(), path), view);
}
}
}
});
}
mActivatedPosition = position;
return rootView;
}
}

View file

@ -31,8 +31,8 @@ import android.support.v4.app.Fragment;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -365,7 +365,7 @@ public class NavigationDrawerFragment extends Fragment {
}
private ActionBar getActionBar() {
return ((ActionBarActivity) getActivity()).getSupportActionBar();
return ((AppCompatActivity) getActivity()).getSupportActionBar();
}
/**

View file

@ -22,7 +22,7 @@
package de.arcus.playmusicexporter2.items;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import de.arcus.framework.utils.SelectionList;
@ -62,7 +62,7 @@ public class SelectedTrackList extends SelectionList<SelectedTrack> {
* @return The new action mode callback
*/
@Override
protected ActionMode.Callback createActionMode(ActionBarActivity activity) {
protected ActionMode.Callback createActionMode(AppCompatActivity activity) {
return new ActionModeTitle(activity, this);
}
}

View file

@ -38,12 +38,14 @@ public class PlayMusicExporterSettings extends AppSettings {
// Preference constants
public static final String PREF_ID3 = "pref_id3";
public static final String PREF_ID3_ARTWORK_SIZE = "pref_id3_artwork_size";
public static final String PREF_EXPORT_PATH = "pref_export_path";
public static final String PREF_STRUCTURE_ALBUMS = "pref_structure_albums";
public 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";
/**
* Creates a new instance of PlayMusicExporterSettings that access to the default settings file
* @param context Context of the app
@ -57,6 +59,10 @@ public class PlayMusicExporterSettings extends AppSettings {
if (!contains(PREF_ID3))
setString(PREF_ID3, "id3_with_cover");
// ID3 artwork settings
if (!contains(PREF_ID3_ARTWORK_SIZE))
setInt(PREF_ID3_ARTWORK_SIZE, 512);
// Export path
if (!contains(PREF_EXPORT_PATH))
setString(PREF_EXPORT_PATH, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath());

View file

@ -23,21 +23,17 @@
package de.arcus.playmusicexporter2.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
import de.arcus.framework.superuser.SuperUserCommand;
import de.arcus.framework.superuser.SuperUserCommandCallback;
import de.arcus.framework.superuser.SuperUserTools;
import de.arcus.playmusicexporter2.R;
import de.arcus.playmusiclib.ArtworkLoader;
import de.arcus.playmusiclib.ArtworkLoaderCallback;
/**
* Class to load Artworks
* Class to load artworks
*/
public class ImageViewLoader {
public class ArtworkViewLoader {
/**
* A weak reference to the image view
*/
@ -60,10 +56,16 @@ public class ImageViewLoader {
*/
private String mImagePath;
/**
* Url of the image
*/
private String mImageUrl;
/**
* Path of the new image which will be loaded after the current loading is completed
*/
private String mNewImagePath;
private String mNewImageUrl;
/**
* The default image of the image view
@ -83,14 +85,14 @@ public class ImageViewLoader {
* @param path The file path
* @param defaultImage The default image in case the image could not be loaded
*/
public static void loadImage(ImageView imageView, String path, int defaultImage) {
public static void loadImage(ImageView imageView, String path, String url, int defaultImage) {
// Checks for an old artwork loader on this image view
ImageViewLoader imageViewLoader = (ImageViewLoader)imageView.getTag();
ArtworkViewLoader imageViewLoader = (ArtworkViewLoader)imageView.getTag();
if (path == null) path = "";
if (imageViewLoader == null) {
imageViewLoader = new ImageViewLoader(imageView, path, defaultImage);
imageViewLoader = new ArtworkViewLoader(imageView, path, url, defaultImage);
// Save the loader in the tag
// If someone wants to load another artwork to this view
@ -99,7 +101,7 @@ public class ImageViewLoader {
imageViewLoader.loadImage();
} else {
// Updates the old loader
imageViewLoader.updateImage(path);
imageViewLoader.updateImage(path, url);
}
}
@ -108,9 +110,10 @@ public class ImageViewLoader {
* @param imageView The image view we want to set
* @param path The path to load
*/
private ImageViewLoader(ImageView imageView, String path, int defaultImage) {
private ArtworkViewLoader(ImageView imageView, String path, String url, int defaultImage) {
mImageView = new WeakReference<>(imageView);
mImagePath = path;
mImageUrl = url;
mDefaultImage = defaultImage;
}
@ -120,87 +123,68 @@ public class ImageViewLoader {
private void loadImage() {
// Show the default icon while loading
final ImageView imageViewDefault = mImageView.get();
int maximalArtworkSize = 0;
if (imageViewDefault != null) {
// The maximum artwork size
maximalArtworkSize = imageViewDefault.getWidth();
// Sets the bitmap in the UI thread
Runnable runnable = new Runnable() {
@Override
public void run() {
// Default icon
imageViewDefault.setImageResource(mDefaultImage);
}
};
imageViewDefault.post(runnable);
}
if (!TextUtils.isEmpty(mImagePath)) {
mIsLoading = true;
// Start loading
mIsLoading = true;
// Be careful! Don't scroll to fast! This will spam the superuser to do list!
SuperUserTools.fileReadToByteArrayAsync(mImagePath, new SuperUserCommandCallback() {
@Override
public void onFinished(SuperUserCommand command) {
// Load the artwork
ArtworkLoader.loadArtworkAsync(mImagePath, mImageUrl, maximalArtworkSize, new ArtworkLoaderCallback() {
@Override
public void onFinished(final Bitmap bitmap) {
final ImageView imageView = mImageView.get();
final ImageView imageView = mImageView.get();
if (imageView != null) {
// Success
if (command.commandWasSuccessful()) {
// Binary data
byte[] bitmapData = command.getStandardOutputBinary();
// Loads the bitmap
try {
// We already want to load a new image, so we don't need to set this
if (mNewImagePath == null) {
// Loads the bitmap
final Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length);
// Sets the bitmap in the UI thread
Runnable runnable = new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
};
imageView.post(runnable);
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
// Sets the bitmap in the UI thread
Runnable runnable = new Runnable() {
@Override
public void run() {
// File not found
imageView.setImageResource(R.drawable.cd_case);
}
};
imageView.post(runnable);
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);
}
}
mIsLoading = false;
// Loads the next image
if (mNewImagePath != null) {
mImagePath = mNewImagePath;
mNewImagePath = null;
loadImage();
}
};
imageView.post(runnable);
}
});
}
// Loading is done
mIsLoading = false;
// Loads the next image
if (mNewImagePath != null) {
mImagePath = mNewImagePath;
mImageUrl = mNewImageUrl;
mNewImagePath = null;
mNewImageUrl = null;
loadImage();
}
}
});
}
/**
* Loads a new artwork
* @param path New artwork path
*/
private void updateImage(String path) {
private void updateImage(String path, String url) {
// The same artwork; nothing to do
if (path.equals(mImagePath)) {
return;
@ -208,8 +192,10 @@ public class ImageViewLoader {
if (mIsLoading) {
mNewImagePath = path;
mNewImageUrl = url;
} else {
mImagePath = path;
mImageUrl = url;
loadImage();
}
}

View file

@ -21,6 +21,8 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/track_detail_container"
android:layout_width="match_parent" android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/track_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TrackDetailActivity" tools:ignore="MergeRootFrame" />

View file

@ -22,16 +22,18 @@
<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TrackListActivity">
<!-- As the main content view, the view below consumes the entire
space available using match_parent in both dimensions. -->
<fragment android:id="@+id/track_list"
android:name="de.arcus.playmusicexporter2.fragments.MusicTrackListFragment"
<fragment android:id="@+id/fragment_main"
android:name="de.arcus.playmusicexporter2.fragments.MusicContainerListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TrackListActivity"

View file

@ -44,8 +44,8 @@
For more on layout aliases, see:
http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters
-->
<fragment android:id="@+id/track_list"
android:name="de.arcus.playmusicexporter2.fragments.MusicTrackListFragment" android:layout_width="0dp"
<fragment android:id="@+id/fragment_main"
android:name="de.arcus.playmusicexporter2.fragments.MusicContainerListFragment" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_weight="1.5"
tools:layout="@android:layout/list_content" />

View file

@ -57,4 +57,6 @@
<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_summary">Wenn die Größe des Artworks größer als der eingestellte Wert ist, wird das Artwork auf diesen Wert runter skalliert.</string>
</resources>

View file

@ -66,6 +66,26 @@
<string name="settings_export_id3_without_cover">Metadata without cover</string>
<string name="settings_export_id3_disabled">No metadata</string>
<string name="settings_export_id3_artwork_size">Maximum artwork size</string>
<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_id3_artwork_size_128px" translatable="false">128x128px</string>
<string name="settings_export_id3_artwork_size_256px" translatable="false">256x256px</string>
<string name="settings_export_id3_artwork_size_512px" translatable="false">512x512px</string>
<string name="settings_export_id3_artwork_size_1024px" translatable="false">1024x1024px</string>
<string-array name="settings_export_id3_artwork_size_value_names" translatable="false">
<item>@string/settings_export_id3_artwork_size_128px</item>
<item>@string/settings_export_id3_artwork_size_256px</item>
<item>@string/settings_export_id3_artwork_size_512px</item>
<item>@string/settings_export_id3_artwork_size_1024px</item>
</string-array>
<string-array name="settings_export_id3_artwork_size_values" translatable="false">
<item>128</item>
<item>256</item>
<item>512</item>
<item>1024</item>
</string-array>
<string name="settings_export_path_custom">Custom path</string>
<string-array name="settings_export_id3_value_names">
<item>@string/settings_export_id3_with_cover</item>
@ -78,6 +98,8 @@
<item>id3_disabled</item>
</string-array>
<string name="settings_open_homepage_title" translatable="false">David-Schulte.de</string>
<string name="settings_open_homepage_url" translatable="false"><![CDATA[http://www.david-schulte.de/]]></string>

View file

@ -45,6 +45,16 @@
android:entries="@array/settings_export_id3_value_names"
android:entryValues="@array/settings_export_id3_values"
/>
<!-- ID3 artwork size -->
<ListPreference android:title="@string/settings_export_id3_artwork_size"
android:key="preference_id3_artwork_size"
android:summary="@string/settings_export_id3_artwork_size_summary"
android:defaultValue="512"
android:entries="@array/settings_export_id3_artwork_size_value_names"
android:entryValues="@array/settings_export_id3_artwork_size_values"
/>
</PreferenceCategory>
<!-- Thanks to -->

View file

@ -88,7 +88,7 @@
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="mp3agic-0.8.4-SNAPSHOT" level="project" />
<orderEntry type="library" exported="" name="support-annotations-21.0.3" level="project" />
<orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" />
<orderEntry type="module" module-name="framework" exported="" />
</component>
</module>

View file

@ -0,0 +1,109 @@
/*
* 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.playmusiclib;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.text.TextUtils;
import java.net.URL;
import de.arcus.framework.logger.Logger;
import de.arcus.framework.superuser.SuperUserTools;
import de.arcus.framework.utils.ImageTools;
/**
* This class contains methods to load the artworks from the Play Music cache or from the internet
*/
public class ArtworkLoader {
/**
* Loads an artwork
* @param artworkPath The local filepath
* @param artworkUrl The remote url
* @param artworkSize The size
* @return The loaded bitmap or null if it failed
*/
public static Bitmap loadArtwork(String artworkPath, String artworkUrl, int artworkSize) {
Bitmap bitmap = null;
// The local path is set
if (!TextUtils.isEmpty(artworkPath)) {
// Tries to load the bitmap
try {
// Load the local file
byte[] bitmapData = SuperUserTools.fileReadToByteArray(artworkPath);
bitmap = ImageTools.decodeByteArraySubsampled(bitmapData, artworkSize, artworkSize);
} catch (Exception e) {
// Error
Logger.getInstance().logError("LoadArtwork", e.toString());
}
}
// The bitmap is not loaded
if (bitmap == null) {
// Url is set
if (!TextUtils.isEmpty(artworkUrl)) {
// Tries to load the artwork via internet
try {
URL url = new URL(artworkUrl);
bitmap = BitmapFactory.decodeStream(url.openStream());
} catch (Exception e) {
// Error
Logger.getInstance().logError("LoadArtwork", e.toString());
}
}
}
return bitmap;
}
/**
* Loads an artwork
* @param artworkPath The local filepath
* @param artworkUrl The remote url
* @param artworkSize The size
* @param callback The callback
*/
public static void loadArtworkAsync(final String artworkPath, final String artworkUrl, final int artworkSize, final ArtworkLoaderCallback callback) {
// The main handler
final Handler handler = new Handler();
// Create a thread to run the artwork loader
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = loadArtwork(artworkPath, artworkUrl, artworkSize);
// Call the callback event in the main thread
handler.post(new Runnable() {
@Override
public void run() {
callback.onFinished(bitmap);
}
});
}
}).start();
}
}

View file

@ -0,0 +1,37 @@
/*
* 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.playmusiclib;
import android.graphics.Bitmap;
/**
* This is a callback interface which is called
* when an artwork was loaded from the local data or the internet
*/
public interface ArtworkLoaderCallback {
/**
* Callback event
* @param bitmap The bitmap or null if it failed
*/
void onFinished(Bitmap bitmap);
}

View file

@ -27,6 +27,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.os.Environment;
import android.text.TextUtils;
@ -38,6 +39,7 @@ import com.mpatric.mp3agic.ID3v23Tag;
import com.mpatric.mp3agic.ID3v24Tag;
import com.mpatric.mp3agic.Mp3File;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@ -195,6 +197,46 @@ public class PlayMusicManager {
mID3EnableArtwork = id3EnableArtwork;
}
/**
* The ID3 artwork format (eg. JPEG or PNG)
*/
private Bitmap.CompressFormat mID3ArtworkFormat = Bitmap.CompressFormat.JPEG;
/**
* @return Gets the current artwork format
*/
public Bitmap.CompressFormat getID3ArtworkFormat() {
return mID3ArtworkFormat;
}
/**
* @param id3ArtworkFormat Sets the artwork format for the id3 tag
*/
public void setID3ArtworkFormat(Bitmap.CompressFormat id3ArtworkFormat) {
mID3ArtworkFormat = id3ArtworkFormat;
}
/**
* The ID3 artwork maximum size (0 = use original size)
*/
private int mID3ArtworkMaximumSize = 512;
/**
* @return Gets the current artwork format
*/
public int getID3ArtworkMaximumSize() {
return mID3ArtworkMaximumSize;
}
/**
* @param id3ArtworkMaximumSize Sets the artwork maximum size of the artwork.
* If the original artwork is larger than this value the app will
* sample it down. (0 = use original size)
*/
public void setID3ArtworkMaximumSize(int id3ArtworkMaximumSize) {
mID3ArtworkMaximumSize = id3ArtworkMaximumSize;
}
/**
* If this is set the exporter will also adds ID3v1 tags
*/
@ -521,7 +563,7 @@ public class PlayMusicManager {
}
// It can't be null
ID3v2 tagID3v2 = null;
final ID3v2 tagID3v2;
// Creates the requested version
switch(mID3v2Version) {
@ -534,6 +576,9 @@ public class PlayMusicManager {
case ID3v24:
tagID3v2 = new ID3v24Tag();
break;
default:
tagID3v2 = null;
break;
}
@ -558,29 +603,21 @@ public class PlayMusicManager {
// Add the artwork to the meta data
if (mID3EnableArtwork) {
String artworkPath = musicTrack.getArtworkPath();
String artworkLocation = musicTrack.getArtworkLocation();
if (artworkPath != null) {
// Load the artwork
Bitmap bitmap = ArtworkLoader.loadArtwork(artworkPath, artworkLocation, mID3ArtworkMaximumSize);
// Reads the artwork with root permissions (maybe it is in /data)
byte[] artworkData = SuperUserTools.fileReadToByteArray(artworkPath);
if (artworkData != null) {
// The file extension is always .jpg even if the image is a PNG file,
// so we need to check the magic number
if (bitmap != null) {
// JPEG is default
String mimeType = "image/jpeg";
// 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);
// Check for other image formats
if (artworkData.length > 4) {
// PNG-Header
if (artworkData[0] == -119 && artworkData[1] == 80 && artworkData[2] == 78 && artworkData[3] == 71) {
mimeType = "image/png";
}
}
// Adds the artwork to the meta data
tagID3v2.setAlbumImage(artworkData, mimeType);
}
// Adds the artwork to the meta data
tagID3v2.setAlbumImage(artworkDataStream.toByteArray(), mimeType);
}
}
@ -626,9 +663,9 @@ public class PlayMusicManager {
// Failed
return false;
}
/**
* Deletes all cache files
*/

View file

@ -43,6 +43,7 @@ public class AlbumDataSource extends DataSource<Album> {
private final static String COLUMN_ALBUM_ID = "MUSIC.AlbumId";
private final static String COLUMN_ALBUM = "MUSIC.Album";
private final static String COLUMN_ALBUM_ARTIST = "MUSIC.AlbumArtist";
private final static String COLUMN_ARTWORK_LOCATION = "MUSIC.AlbumArtLocation";
private final static String COLUMN_ALBUM_ARTWORK_FILE = "(SELECT ARTWORK_CACHE.LocalLocation FROM MUSIC AS MUSIC2 LEFT JOIN ARTWORK_CACHE ON MUSIC2.AlbumArtLocation = ARTWORK_CACHE.RemoteLocation WHERE MUSIC2.AlbumID = MUSIC.AlbumID AND ARTWORK_CACHE.RemoteLocation IS NOT NULL LIMIT 1) AS ArtistArtworkPath";
private final static String COLUMN_TITLE = "MUSIC.Title";
@ -50,7 +51,7 @@ public class AlbumDataSource extends DataSource<Album> {
// All columns
private final static String[] COLUMNS_ALL = {COLUMN_ALBUM_ID, COLUMN_ALBUM,
COLUMN_ALBUM_ARTIST, COLUMN_ALBUM_ARTWORK_FILE};
COLUMN_ALBUM_ARTIST, COLUMN_ARTWORK_LOCATION, COLUMN_ALBUM_ARTWORK_FILE};
/**
* If this is set the data source will only load offline tracks
@ -165,6 +166,7 @@ public class AlbumDataSource extends DataSource<Album> {
instance.setAlbumId(cursor.getLong(getColumnsIndex(COLUMNS_ALL, COLUMN_ALBUM_ID)));
instance.setAlbum(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ALBUM)));
instance.setAlbumArtist(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ALBUM_ARTIST)));
instance.setArtworkLocation(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTWORK_LOCATION)));
instance.setArtworkFile(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ALBUM_ARTWORK_FILE)));
return instance;

View file

@ -41,10 +41,11 @@ public class ArtistDataSource extends DataSource<Artist> {
// All fields
private final static String COLUMN_ARTIST_ID = "ArtistId";
private final static String COLUMN_ARTIST = "Artist";
private final static String COLUMN_ARTWORK_LOCATION = "AlbumArtLocation";
private final static String COLUMN_ARTWORK_FILE = "(SELECT ARTWORK_CACHE.LocalLocation FROM MUSIC AS MUSIC2 LEFT JOIN ARTWORK_CACHE ON ARTWORK_CACHE.RemoteLocation = MUSIC.AlbumArtLocation WHERE MUSIC2.ArtistId = MUSIC.ArtistId AND ARTWORK_CACHE.LocalLocation IS NOT NULL LIMIT 1) AS ArtworkFile";
// All columns
private final static String[] COLUMNS_ALL = { COLUMN_ARTIST_ID, COLUMN_ARTIST, COLUMN_ARTWORK_FILE };
private final static String[] COLUMNS_ALL = { COLUMN_ARTIST_ID, COLUMN_ARTIST, COLUMN_ARTWORK_LOCATION, COLUMN_ARTWORK_FILE };
/**
@ -131,6 +132,7 @@ public class ArtistDataSource extends DataSource<Artist> {
// Read all properties from the data row
instance.setArtistId(cursor.getLong(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTIST_ID)));
instance.setArtist(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTIST)));
instance.setArtworkLocation(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTWORK_LOCATION)));
instance.setArtworkFile(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTWORK_FILE)));
return instance;

View file

@ -63,13 +63,14 @@ public class MusicTrackDataSource extends DataSource<MusicTrack> {
private final static String COLUMN_CLIENT_ID = "MUSIC.ClientId";
private final static String COLUMN_SOURCE_ID = "MUSIC.SourceId";
private final static String COLUMN_CPDATA = "MUSIC.CpData";
private final static String COLUMN_ARTWORK_LOCATION = "MUSIC.AlbumArtLocation";
private final static String COLUMN_ARTWORK_FILE = "(SELECT LocalLocation FROM artwork_cache WHERE artwork_cache.RemoteLocation = AlbumArtLocation) AS ArtworkFile";
// All columns
private final static String[] COLUMNS_ALL = { COLUMN_ID, COLUMN_SIZE,
COLUMN_LOCALCOPYPATH, COLUMN_LOCALCOPYTYPE, COLUMN_LOCALCOPYSTORAGETYPE, COLUMN_TITLE, COLUMN_ARTIST_ID, COLUMN_ARTIST, COLUMN_ALBUM_ARTIST,
COLUMN_ALBUM, COLUMN_GENRE, COLUMN_YEAR, COLUMN_TRACK_NUMBER, COLUMN_DISC_NUMBER, COLUMN_DURATION, COLUMN_RATING,
COLUMN_ALBUM_ID, COLUMN_CLIENT_ID, COLUMN_SOURCE_ID, COLUMN_ARTWORK_FILE, COLUMN_CPDATA };
COLUMN_ALBUM_ID, COLUMN_CLIENT_ID, COLUMN_SOURCE_ID, COLUMN_ARTWORK_LOCATION, COLUMN_ARTWORK_FILE, COLUMN_CPDATA };
/**
* If this is set the data source will only load offline tracks
@ -189,6 +190,7 @@ public class MusicTrackDataSource extends DataSource<MusicTrack> {
instance.setClientId(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_CLIENT_ID)));
instance.setSourceId(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_SOURCE_ID)));
instance.setCpData(cursor.getBlob(getColumnsIndex(COLUMNS_ALL, COLUMN_CPDATA)));
instance.setArtworkLocation(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTWORK_LOCATION)));
instance.setArtworkFile(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTWORK_FILE)));
// Sets the container information

View file

@ -43,11 +43,12 @@ public class PlaylistDataSource extends DataSource<Playlist> {
private final static String COLUMN_NAME = "LISTS.Name";
private final static String COLUMN_LIST_TYPE = "LISTS.ListType";
private final static String COLUMN_OWNER_NAME = "LISTS.OwnerName";
private final static String COLUMN_ARTWORK_LOCATION = "(SELECT MUSIC.AlbumArtLocation FROM LISTITEMS LEFT JOIN MUSIC ON MUSIC.Id = LISTITEMS.MusicId WHERE LISTITEMS.ListId = LISTS.Id) AS AlbumArtLocation";
private final static String COLUMN_ARTWORK_FILE = "(SELECT ARTWORK_CACHE.LocalLocation FROM LISTITEMS LEFT JOIN MUSIC ON MUSIC.Id = LISTITEMS.MusicId LEFT JOIN ARTWORK_CACHE ON ARTWORK_CACHE.RemoteLocation = MUSIC.AlbumArtLocation WHERE LISTITEMS.ListId = LISTS.Id AND ARTWORK_CACHE.LocalLocation IS NOT NULL LIMIT 1) AS ArtworkFile";
// All columns
private final static String[] COLUMNS_ALL = { COLUMN_ID, COLUMN_NAME,
COLUMN_LIST_TYPE, COLUMN_OWNER_NAME, COLUMN_ARTWORK_FILE};
COLUMN_LIST_TYPE, COLUMN_OWNER_NAME, COLUMN_ARTWORK_LOCATION, COLUMN_ARTWORK_FILE};
/**
@ -129,6 +130,7 @@ public class PlaylistDataSource extends DataSource<Playlist> {
instance.setName(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_NAME)));
instance.setListType(cursor.getLong(getColumnsIndex(COLUMNS_ALL, COLUMN_LIST_TYPE)));
instance.setOwnerName(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_OWNER_NAME)));
instance.setArtworkLocation(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTWORK_LOCATION)));
instance.setArtworkFile(cursor.getString(getColumnsIndex(COLUMNS_ALL, COLUMN_ARTWORK_FILE)));
return instance;

View file

@ -0,0 +1,29 @@
/*
* 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.playmusiclib.items;
/**
* Created by david on 14.05.15.
*/
public interface ArtworkEntry {
}

View file

@ -37,6 +37,7 @@ public class MusicTrack {
private String mSourceFile;
private String mArtworkPath;
private String mArtworkLocation;
/**
* The manager
@ -369,6 +370,20 @@ public class MusicTrack {
*/
private long mContainerPosition;
/**
* Gets the artwork url
*/
public String getArtworkLocation() {
return mArtworkLocation;
}
/**
* Sets the artwork url
*/
public void setArtworkLocation(String artworkLocation) {
mArtworkLocation = artworkLocation;
}
/**
* @return Gets the position in the container
*/

View file

@ -59,7 +59,12 @@ public abstract class MusicTrackList {
protected String mArtworkFile;
/**
* The complet path of the artwork
* The url of the artwork
*/
protected String mArtworkLocation;
/**
* The complete path of the artwork
*/
protected String mArtworkPath;
@ -89,6 +94,20 @@ public abstract class MusicTrackList {
mArtworkFile = artworkFile;
}
/**
* Gets the artwork url
*/
public String getArtworkLocation() {
return mArtworkLocation;
}
/**
* Sets the artwork url
*/
public void setArtworkLocation(String artworkLocation) {
mArtworkLocation = artworkLocation;
}
/**
* Loads all tracks from this list.
* Must be overwritten in all extended classes