Improved keeping user informed on sync progress
Implemented binding to LibrarySyncService to check if there are items currently syncing or queued to sync. This makes it possible to inform user of background sync processes. Created two abstract classes to reduce code duplication and check if LibrarySyncService has any items queued which are currently being displayed. Fixed issue with SwipeRefreshLayout from appcompat library, which does not always show the refresh animation when refresh is set to true.
This commit is contained in:
parent
dab9bc055e
commit
499e54f2cf
|
@ -16,16 +16,23 @@
|
|||
package org.xbmc.kore.service;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
|
||||
import org.xbmc.kore.host.HostInfo;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
|
@ -44,6 +51,7 @@ import org.xbmc.kore.utils.Utils;
|
|||
import java.lang.System;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
@ -89,6 +97,10 @@ public class LibrarySyncService extends Service {
|
|||
private Handler callbackHandler;
|
||||
private HandlerThread handlerThread;
|
||||
|
||||
private ArrayList<SyncOrchestrator> syncOrchestrators;
|
||||
|
||||
private final IBinder serviceBinder = new LocalBinder();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Create a Handler Thread to process callback calls after the Xbmc method call
|
||||
|
@ -98,6 +110,8 @@ public class LibrarySyncService extends Service {
|
|||
// Get the HandlerThread's Looper and use it for our Handler
|
||||
callbackHandler = new Handler(handlerThread.getLooper());
|
||||
// Check which libraries to update and call the corresponding methods on Xbmc
|
||||
|
||||
syncOrchestrators = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,12 +121,12 @@ public class LibrarySyncService extends Service {
|
|||
// to not interfere with the normal application usage of it (namely calls to disconnect
|
||||
// and usage of the socket).
|
||||
HostInfo hostInfo = HostManager.getInstance(this).getHostInfo();
|
||||
HostConnection hostConnection = new HostConnection(hostInfo);
|
||||
hostConnection.setProtocol(HostConnection.PROTOCOL_HTTP);
|
||||
|
||||
SyncOrchestrator syncOrchestrator = new SyncOrchestrator(this, startId, hostConnection,
|
||||
SyncOrchestrator syncOrchestrator = new SyncOrchestrator(this, startId, hostInfo,
|
||||
callbackHandler, getContentResolver());
|
||||
|
||||
syncOrchestrators.add(syncOrchestrator);
|
||||
|
||||
// Get the request parameters that we should pass when calling back the caller
|
||||
Bundle syncExtras = intent.getBundleExtra(SYNC_EXTRAS);
|
||||
|
||||
|
@ -167,8 +181,7 @@ public class LibrarySyncService extends Service {
|
|||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
// We don't provide binding, so return null
|
||||
return null;
|
||||
return serviceBinder;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
|
@ -182,40 +195,70 @@ public class LibrarySyncService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
public class LocalBinder extends Binder {
|
||||
public LibrarySyncService getService() {
|
||||
return LibrarySyncService.this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hostInfo host information for which to get items currently syncing
|
||||
* @return currently syncing syncitems for given hostInfo
|
||||
*/
|
||||
public ArrayList<SyncItem> getItemsSyncing(HostInfo hostInfo) {
|
||||
ArrayList<SyncItem> syncItems = new ArrayList<>();
|
||||
for( SyncOrchestrator orchestrator : syncOrchestrators) {
|
||||
if( orchestrator.getHostInfo().getId() == hostInfo.getId() ) {
|
||||
syncItems.addAll(orchestrator.getSyncItems());
|
||||
return syncItems;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orchestrator for a list os SyncItems
|
||||
* Keeps a list of SyncItems to sync, and calls each one in order
|
||||
* When finishes cleans up and stops the service by calling stopSelf
|
||||
*/
|
||||
private static class SyncOrchestrator {
|
||||
private class SyncOrchestrator {
|
||||
private ArrayDeque<SyncItem> syncItems;
|
||||
private Service syncService;
|
||||
private final int serviceStartId;
|
||||
private final HostConnection hostConnection;
|
||||
private HostConnection hostConnection;
|
||||
private final HostInfo hostInfo;
|
||||
private final Handler callbackHandler;
|
||||
private final ContentResolver contentResolver;
|
||||
|
||||
private SyncItem currentSyncItem;
|
||||
|
||||
private Iterator<SyncItem> syncItemIterator;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param syncService Service on which to call {@link #stopSelf()} when finished
|
||||
* @param startId Service startid to use when calling {@link #stopSelf()}
|
||||
* @param hostConnection Host connection to use
|
||||
* @param hostInfo Host from which to sync
|
||||
* @param callbackHandler Handler on which to post callbacks
|
||||
* @param contentResolver Content resolver
|
||||
*/
|
||||
public SyncOrchestrator(Service syncService, final int startId,
|
||||
final HostConnection hostConnection,
|
||||
final HostInfo hostInfo,
|
||||
final Handler callbackHandler,
|
||||
final ContentResolver contentResolver) {
|
||||
this.syncService = syncService;
|
||||
this.syncItems = new ArrayDeque<SyncItem>();
|
||||
this.serviceStartId = startId;
|
||||
this.hostConnection = hostConnection;
|
||||
this.hostInfo = hostInfo;
|
||||
this.callbackHandler = callbackHandler;
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
public HostInfo getHostInfo() {
|
||||
return hostInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this item to the sync list
|
||||
* @param syncItem Sync item
|
||||
|
@ -224,6 +267,10 @@ public class LibrarySyncService extends Service {
|
|||
syncItems.add(syncItem);
|
||||
}
|
||||
|
||||
public ArrayDeque<SyncItem> getSyncItems() {
|
||||
return syncItems;
|
||||
}
|
||||
|
||||
private long startTime = -1;
|
||||
private long partialStartTime;
|
||||
|
||||
|
@ -232,16 +279,19 @@ public class LibrarySyncService extends Service {
|
|||
*/
|
||||
public void startSync() {
|
||||
startTime = System.currentTimeMillis();
|
||||
hostConnection = new HostConnection(hostInfo);
|
||||
hostConnection.setProtocol(HostConnection.PROTOCOL_HTTP);
|
||||
syncItemIterator = syncItems.iterator();
|
||||
nextSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the next item on the sync list, or cleans up if it is finished.
|
||||
*/
|
||||
public void nextSync() {
|
||||
if (syncItems.size() > 0) {
|
||||
private void nextSync() {
|
||||
if (syncItemIterator.hasNext()) {
|
||||
partialStartTime = System.currentTimeMillis();
|
||||
currentSyncItem = syncItems.poll();
|
||||
currentSyncItem = syncItemIterator.next();
|
||||
currentSyncItem.sync(this, hostConnection, callbackHandler, contentResolver);
|
||||
} else {
|
||||
LogUtils.LOGD(TAG, "Sync finished for all items. Total time: " +
|
||||
|
@ -249,6 +299,8 @@ public class LibrarySyncService extends Service {
|
|||
// No more syncs, cleanup.
|
||||
// No need to disconnect, as this is HTTP
|
||||
//hostConnection.disconnect();
|
||||
|
||||
syncOrchestrators.remove(this);
|
||||
syncService.stopSelf(serviceStartId);
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +308,7 @@ public class LibrarySyncService extends Service {
|
|||
/**
|
||||
* One of the syync items finish syncing
|
||||
*/
|
||||
public void syncItemFinished() {
|
||||
private void syncItemFinished() {
|
||||
LogUtils.LOGD(TAG, "Sync finished for item: " + currentSyncItem.getDescription() +
|
||||
". Total time: " + (System.currentTimeMillis() - partialStartTime));
|
||||
|
||||
|
@ -264,6 +316,9 @@ public class LibrarySyncService extends Service {
|
|||
.post(new MediaSyncEvent(currentSyncItem.getSyncType(),
|
||||
currentSyncItem.getSyncExtras(),
|
||||
MediaSyncEvent.STATUS_SUCCESS));
|
||||
|
||||
syncItems.remove(currentSyncItem);
|
||||
|
||||
nextSync();
|
||||
}
|
||||
|
||||
|
@ -272,7 +327,7 @@ public class LibrarySyncService extends Service {
|
|||
* @param errorCode Error code
|
||||
* @param description Description
|
||||
*/
|
||||
public void syncItemFailed(int errorCode, String description) {
|
||||
private void syncItemFailed(int errorCode, String description) {
|
||||
LogUtils.LOGD(TAG, "A Sync item has got an error. Sync item: " +
|
||||
currentSyncItem.getDescription() +
|
||||
". Error description: " + description);
|
||||
|
@ -291,7 +346,7 @@ public class LibrarySyncService extends Service {
|
|||
/**
|
||||
* Represent an item that can be synced
|
||||
*/
|
||||
private interface SyncItem {
|
||||
public interface SyncItem {
|
||||
/**
|
||||
* Syncs an item from the XBMC host to the local database
|
||||
* @param orchestrator Orchestrator to call when finished
|
||||
|
|
|
@ -15,8 +15,14 @@
|
|||
*/
|
||||
package org.xbmc.kore.service;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
|
||||
import org.xbmc.kore.host.HostInfo;
|
||||
import org.xbmc.kore.jsonrpc.type.VideoType;
|
||||
import org.xbmc.kore.jsonrpc.type.AudioType;
|
||||
import org.xbmc.kore.jsonrpc.type.LibraryType;
|
||||
|
@ -31,6 +37,10 @@ import java.util.List;
|
|||
*/
|
||||
public class SyncUtils {
|
||||
|
||||
public interface OnServiceListener {
|
||||
void onServiceConnected(LibrarySyncService librarySyncService);
|
||||
}
|
||||
|
||||
public static final String LIST_DELIMETER = ", ";
|
||||
|
||||
/**
|
||||
|
@ -336,7 +346,7 @@ public class SyncUtils {
|
|||
* @return {@link android.content.ContentValues} with the music video values
|
||||
*/
|
||||
public static ContentValues contentValuesFromMusicVideo(int hostId,
|
||||
VideoType.DetailsMusicVideo musicVideo) {
|
||||
VideoType.DetailsMusicVideo musicVideo) {
|
||||
ContentValues musicVideoValues = new ContentValues();
|
||||
musicVideoValues.put(MediaContract.MusicVideosColumns.HOST_ID, hostId);
|
||||
musicVideoValues.put(MediaContract.MusicVideosColumns.MUSICVIDEOID, musicVideo.musicvideoid);
|
||||
|
@ -401,4 +411,69 @@ public class SyncUtils {
|
|||
return musicVideoValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds to {@link LibrarySyncService} and calls {@link OnServiceListener#onServiceConnected(LibrarySyncService)} when connected
|
||||
* @param context {@link Context}
|
||||
* @param listener {@link OnServiceListener}
|
||||
* @return {@link ServiceConnection} to be able to disconnect the connection
|
||||
*/
|
||||
public static ServiceConnection connectToLibrarySyncService(Context context, final OnServiceListener listener) {
|
||||
Intent intent = new Intent(context, LibrarySyncService.class);
|
||||
|
||||
final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className,
|
||||
IBinder service) {
|
||||
|
||||
LibrarySyncService.LocalBinder binder = (LibrarySyncService.LocalBinder) service;
|
||||
LibrarySyncService librarySyncService = binder.getService();
|
||||
|
||||
listener.onServiceConnected(librarySyncService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName arg0) {
|
||||
}
|
||||
};
|
||||
|
||||
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
|
||||
return serviceConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects from a running LibrarySyncService
|
||||
* @param context {@link Context}
|
||||
* @param serviceConnection {@link ServiceConnection} that was used to connect/bind to the service
|
||||
*/
|
||||
public static void disconnectFromLibrarySyncService(Context context, ServiceConnection serviceConnection) {
|
||||
context.unbindService(serviceConnection);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a running LibrarySyncService is syncing any given SyncType from a specific host
|
||||
* @param service running LibrarySyncService. Use {@link #connectToLibrarySyncService(Context, OnServiceListener)} to connect to a running LibrarySyncService
|
||||
* @param hostInfo host to check for sync items currently running or queued
|
||||
* @param syncTypes sync types to check
|
||||
* @return true if any of the given syncTypes is running or queued, false otherwise
|
||||
*/
|
||||
public static boolean isLibrarySyncing(LibrarySyncService service, HostInfo hostInfo, String... syncTypes) {
|
||||
if (service == null || hostInfo == null || syncTypes == null)
|
||||
return false;
|
||||
|
||||
ArrayList<LibrarySyncService.SyncItem> itemsSyncing = service.getItemsSyncing(hostInfo);
|
||||
if( itemsSyncing == null )
|
||||
return false;
|
||||
|
||||
for (LibrarySyncService.SyncItem syncItem : itemsSyncing) {
|
||||
for( String syncType : syncTypes ) {
|
||||
if (syncItem.getSyncType().equals(syncType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Copyright 2015 Martijn Brekhof. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.xbmc.kore.ui;
|
||||
|
||||
import android.content.ServiceConnection;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.GridView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.host.HostInfo;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
||||
import org.xbmc.kore.service.SyncUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.InjectView;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public abstract class AbstractListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
SyncUtils.OnServiceListener,
|
||||
SearchView.OnQueryTextListener,
|
||||
SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
private ServiceConnection serviceConnection;
|
||||
private CursorAdapter adapter;
|
||||
|
||||
private HostInfo hostInfo;
|
||||
private EventBus bus;
|
||||
|
||||
// Loader IDs
|
||||
private static final int LOADER = 0;
|
||||
|
||||
// The search filter to use in the loader
|
||||
private String searchFilter = null;
|
||||
|
||||
private boolean isSyncing;
|
||||
|
||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
||||
@InjectView(R.id.list) GridView gridView;
|
||||
@InjectView(android.R.id.empty) TextView emptyView;
|
||||
|
||||
abstract protected AdapterView.OnItemClickListener createOnItemClickListener();
|
||||
abstract protected CursorAdapter createAdapter();
|
||||
abstract protected void onSwipeRefresh();
|
||||
abstract protected CursorLoader createCursorLoader();
|
||||
|
||||
/**
|
||||
* Called each time a MediaSyncEvent is received.
|
||||
* @param event
|
||||
*/
|
||||
abstract protected void onSyncProcessEnded(MediaSyncEvent event);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
bus = EventBus.getDefault();
|
||||
HostManager hostManager = HostManager.getInstance(getActivity());
|
||||
hostInfo = hostManager.getHostInfo();
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
gridView.setEmptyView(emptyView);
|
||||
gridView.setOnItemClickListener(createOnItemClickListener());
|
||||
|
||||
// Configure the adapter and start the loader
|
||||
adapter = createAdapter();
|
||||
gridView.setAdapter(adapter);
|
||||
|
||||
getLoaderManager().initLoader(LOADER, null, this);
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
serviceConnection = SyncUtils.connectToLibrarySyncService(getActivity(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
bus.register(this);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
bus.unregister(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
SyncUtils.disconnectFromLibrarySyncService(getActivity(), serviceConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
if (hostInfo != null) {
|
||||
showRefreshAnimation();
|
||||
onSwipeRefresh();
|
||||
} else {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event bus post. Called when the syncing process ended
|
||||
*
|
||||
* @param event Refreshes data
|
||||
*/
|
||||
public void onEventMainThread(MediaSyncEvent event) {
|
||||
onSyncProcessEnded(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search view callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
searchFilter = newText;
|
||||
getLoaderManager().restartLoader(LOADER, null, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String newText) {
|
||||
// All is handled in onQueryTextChange
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
return createCursorLoader();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
adapter.swapCursor(cursor);
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.swipe_down_to_refresh));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||
adapter.swapCursor(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return text entered in searchview
|
||||
*/
|
||||
public String getSearchFilter() {
|
||||
return searchFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to reload the items in the list
|
||||
*/
|
||||
public void refreshList() {
|
||||
getLoaderManager().restartLoader(LOADER, null, this);
|
||||
}
|
||||
|
||||
public void showRefreshAnimation() {
|
||||
/**
|
||||
* Fixes issue with refresh animation not showing when using appcompat library (from version 20?)
|
||||
* See https://code.google.com/p/android/issues/detail?id=77712
|
||||
*/
|
||||
swipeRefreshLayout.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
swipeRefreshLayout.setRefreshing(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2015 Martijn Brekhof. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.xbmc.kore.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
import org.xbmc.kore.jsonrpc.ApiException;
|
||||
import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
||||
import org.xbmc.kore.service.LibrarySyncService;
|
||||
import org.xbmc.kore.service.SyncUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
public abstract class AbstractMusicListFragment extends AbstractListFragment {
|
||||
@Override
|
||||
protected void onSwipeRefresh() {
|
||||
// Start the syncing process
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_MUSIC, true);
|
||||
getActivity().startService(syncIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_ALL_MUSIC)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
refreshList();
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(LibrarySyncService librarySyncService) {
|
||||
if(SyncUtils.isLibrarySyncing(librarySyncService, HostManager.getInstance(getActivity()).getHostInfo(),
|
||||
LibrarySyncService.SYNC_ALL_MUSIC, LibrarySyncService.SYNC_ALL_MUSIC_VIDEOS)) {
|
||||
showRefreshAnimation();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,39 +62,22 @@ import de.greenrobot.event.EventBus;
|
|||
/**
|
||||
* Fragment that presents the albums list
|
||||
*/
|
||||
public class AlbumListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
SearchView.OnQueryTextListener {
|
||||
public class AlbumListFragment extends AbstractMusicListFragment {
|
||||
private static final String TAG = LogUtils.makeLogTag(AlbumListFragment.class);
|
||||
|
||||
public interface OnAlbumSelectedListener {
|
||||
public void onAlbumSelected(int albumId, String albumTitle);
|
||||
}
|
||||
|
||||
// Loader IDs
|
||||
private static final int LOADER_ALBUMS = 0;
|
||||
|
||||
private static final String GENREID = "genreid",
|
||||
ARTISTID = "artistid";
|
||||
|
||||
// The search filter to use in the loader
|
||||
private String searchFilter = null;
|
||||
private int genreId = -1;
|
||||
private int artistId = -1;
|
||||
|
||||
// Movies adapter
|
||||
private CursorAdapter adapter;
|
||||
|
||||
// Activity listener
|
||||
private OnAlbumSelectedListener listenerActivity;
|
||||
|
||||
private HostInfo hostInfo;
|
||||
private EventBus bus;
|
||||
|
||||
@InjectView(R.id.list) GridView albumsGridView;
|
||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
||||
@InjectView(android.R.id.empty) TextView emptyView;
|
||||
|
||||
/**
|
||||
* Create a new instance of this, initialized to show albums of genres
|
||||
|
@ -121,8 +104,47 @@ public class AlbumListFragment extends Fragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
protected AdapterView.OnItemClickListener createOnItemClickListener() {
|
||||
return new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Get the movie id from the tag
|
||||
ViewHolder tag = (ViewHolder) view.getTag();
|
||||
// Notify the activity
|
||||
listenerActivity.onAlbumSelected(tag.albumId, tag.albumTitle);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CursorAdapter createAdapter() {
|
||||
return new AlbumsAdapter(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CursorLoader createCursorLoader() {
|
||||
Uri uri;
|
||||
HostInfo hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
int hostId = hostInfo != null ? hostInfo.getId() : -1;
|
||||
|
||||
if (artistId != -1) {
|
||||
uri = MediaContract.AlbumArtists.buildAlbumsForArtistListUri(hostId, artistId);
|
||||
} else if (genreId != -1) {
|
||||
uri = MediaContract.AlbumGenres.buildAlbumsForGenreListUri(hostId, genreId);
|
||||
} else {
|
||||
uri = MediaContract.Albums.buildAlbumsListUri(hostId);
|
||||
}
|
||||
|
||||
String selection = null;
|
||||
String selectionArgs[] = null;
|
||||
String searchFilter = getSearchFilter();
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection = MediaContract.Albums.TITLE + " LIKE ?";
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
AlbumListQuery.PROJECTION, selection, selectionArgs, AlbumListQuery.SORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -133,43 +155,7 @@ public class AlbumListFragment extends Fragment
|
|||
artistId = getArguments().getInt(ARTISTID, -1);
|
||||
}
|
||||
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
bus = EventBus.getDefault();
|
||||
hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
|
||||
// Pad main content view to overlap with bottom system bar
|
||||
// UIUtils.setPaddingForSystemBars(getActivity(), albumsGridView, false, false, true);
|
||||
// albumsGridView.setClipToPadding(false);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
albumsGridView.setEmptyView(emptyView);
|
||||
albumsGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Get the movie id from the tag
|
||||
ViewHolder tag = (ViewHolder) view.getTag();
|
||||
// Notify the activity
|
||||
listenerActivity.onAlbumSelected(tag.albumId, tag.albumTitle);
|
||||
}
|
||||
});
|
||||
|
||||
// Configure the adapter and start the loader
|
||||
adapter = new AlbumsAdapter(getActivity());
|
||||
albumsGridView.setAdapter(adapter);
|
||||
getLoaderManager().initLoader(LOADER_ALBUMS, null, this);
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
return super.onCreateView(inflater, container, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -188,18 +174,6 @@ public class AlbumListFragment extends Fragment
|
|||
listenerActivity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
bus.register(this);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
bus.unregister(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.media_search, menu);
|
||||
|
@ -210,112 +184,6 @@ public class AlbumListFragment extends Fragment
|
|||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search view callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
searchFilter = newText;
|
||||
getLoaderManager().restartLoader(LOADER_ALBUMS, null, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String newText) {
|
||||
// All is handled in onQueryTextChange
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onRefresh () {
|
||||
if (hostInfo != null) {
|
||||
// Make sure we're showing the refresh
|
||||
swipeRefreshLayout.setRefreshing(true);
|
||||
// Start the syncing process
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_MUSIC, true);
|
||||
getActivity().startService(syncIntent);
|
||||
} else {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event bus post. Called when the syncing process ended
|
||||
*
|
||||
* @param event Refreshes data
|
||||
*/
|
||||
public void onEventMainThread(MediaSyncEvent event) {
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_ALL_MUSIC)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
getLoaderManager().restartLoader(LOADER_ALBUMS, null, this);
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
Uri uri;
|
||||
int hostId = hostInfo != null ? hostInfo.getId() : -1;
|
||||
|
||||
if (artistId != -1) {
|
||||
uri = MediaContract.AlbumArtists.buildAlbumsForArtistListUri(hostId, artistId);
|
||||
} else if (genreId != -1) {
|
||||
uri = MediaContract.AlbumGenres.buildAlbumsForGenreListUri(hostId, genreId);
|
||||
} else {
|
||||
uri = MediaContract.Albums.buildAlbumsListUri(hostId);
|
||||
}
|
||||
|
||||
String selection = null;
|
||||
String selectionArgs[] = null;
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection = MediaContract.Albums.TITLE + " LIKE ?";
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
AlbumListQuery.PROJECTION, selection, selectionArgs, AlbumListQuery.SORT);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
adapter.swapCursor(cursor);
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.no_albums_found_refresh));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||
adapter.swapCursor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Album list query parameters.
|
||||
*/
|
||||
|
|
|
@ -17,18 +17,12 @@ package org.xbmc.kore.ui;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -39,104 +33,63 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.host.HostInfo;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
import org.xbmc.kore.jsonrpc.ApiException;
|
||||
import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
||||
import org.xbmc.kore.provider.MediaContract;
|
||||
import org.xbmc.kore.provider.MediaDatabase;
|
||||
import org.xbmc.kore.service.LibrarySyncService;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.InjectView;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Fragment that presents the artists list
|
||||
*/
|
||||
public class ArtistListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
SearchView.OnQueryTextListener {
|
||||
public class ArtistListFragment extends AbstractMusicListFragment {
|
||||
private static final String TAG = LogUtils.makeLogTag(ArtistListFragment.class);
|
||||
|
||||
public interface OnArtistSelectedListener {
|
||||
public void onArtistSelected(int artistId, String artistName);
|
||||
}
|
||||
|
||||
// Loader IDs
|
||||
private static final int LOADER_ARTISTS = 0;
|
||||
|
||||
// The search filter to use in the loader
|
||||
private String searchFilter = null;
|
||||
|
||||
// Movies adapter
|
||||
private CursorAdapter adapter;
|
||||
|
||||
// Activity listener
|
||||
private OnArtistSelectedListener listenerActivity;
|
||||
|
||||
private HostManager hostManager;
|
||||
private HostInfo hostInfo;
|
||||
private EventBus bus;
|
||||
|
||||
@InjectView(R.id.list) GridView artistsGridView;
|
||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
||||
@InjectView(android.R.id.empty) TextView emptyView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
bus = EventBus.getDefault();
|
||||
hostManager = HostManager.getInstance(getActivity());
|
||||
hostInfo = hostManager.getHostInfo();
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
//UIUtils.setSwipeRefreshLayoutColorScheme(swipeRefreshLayout);
|
||||
|
||||
// Pad main content view to overlap with bottom system bar
|
||||
// UIUtils.setPaddingForSystemBars(getActivity(), artistsGridView, false, false, true);
|
||||
// artistsGridView.setClipToPadding(false);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
artistsGridView.setEmptyView(emptyView);
|
||||
artistsGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
protected AdapterView.OnItemClickListener createOnItemClickListener() {
|
||||
return new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Get the movie id from the tag
|
||||
// Get the artist id from the tag
|
||||
ViewHolder tag = (ViewHolder) view.getTag();
|
||||
// Notify the activity
|
||||
listenerActivity.onArtistSelected(tag.artistId, tag.artistName);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Configure the adapter and start the loader
|
||||
adapter = new ArtistsAdapter(getActivity());
|
||||
artistsGridView.setAdapter(adapter);
|
||||
getLoaderManager().initLoader(LOADER_ARTISTS, null, this);
|
||||
@Override
|
||||
protected CursorAdapter createAdapter() {
|
||||
return new ArtistsAdapter(getActivity());
|
||||
}
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
@Override
|
||||
protected CursorLoader createCursorLoader() {
|
||||
HostInfo hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
Uri uri = MediaContract.Artists.buildArtistsListUri(hostInfo != null ? hostInfo.getId() : -1);
|
||||
|
||||
String selection = null;
|
||||
String selectionArgs[] = null;
|
||||
String searchFilter = getSearchFilter();
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection = MediaContract.ArtistsColumns.ARTIST + " LIKE ?";
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
ArtistListQuery.PROJECTION, selection, selectionArgs, ArtistListQuery.SORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,18 +108,6 @@ public class ArtistListFragment extends Fragment
|
|||
listenerActivity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
bus.register(this);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
bus.unregister(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.media_search, menu);
|
||||
|
@ -177,113 +118,6 @@ public class ArtistListFragment extends Fragment
|
|||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// switch (item.getItemId()) {
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search view callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
searchFilter = newText;
|
||||
getLoaderManager().restartLoader(LOADER_ARTISTS, null, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String newText) {
|
||||
// All is handled in onQueryTextChange
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
if (hostInfo != null) {
|
||||
// Make sure we're showing the refresh
|
||||
swipeRefreshLayout.setRefreshing(true);
|
||||
// Start the syncing process
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_MUSIC, true);
|
||||
getActivity().startService(syncIntent);
|
||||
|
||||
// Toast.makeText(getActivity(),
|
||||
// String.format(getString(R.string.sync_movies_for_host), hostInfo.getName()),
|
||||
// Toast.LENGTH_SHORT)
|
||||
// .show();
|
||||
} else {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event bus post. Called when the syncing process ended
|
||||
*
|
||||
* @param event Refreshes data
|
||||
*/
|
||||
public void onEventMainThread(MediaSyncEvent event) {
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_ALL_MUSIC)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
getLoaderManager().restartLoader(LOADER_ARTISTS, null, this);
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
Uri uri = MediaContract.Artists.buildArtistsListUri(hostInfo != null ? hostInfo.getId() : -1);
|
||||
|
||||
String selection = null;
|
||||
String selectionArgs[] = null;
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection = MediaContract.ArtistsColumns.ARTIST + " LIKE ?";
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
ArtistListQuery.PROJECTION, selection, selectionArgs, ArtistListQuery.SORT);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
adapter.swapCursor(cursor);
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.no_artists_found_refresh));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||
adapter.swapCursor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Artist list query parameters.
|
||||
*/
|
||||
|
@ -317,16 +151,16 @@ public class ArtistListFragment extends Fragment
|
|||
// Get the art dimensions
|
||||
Resources resources = context.getResources();
|
||||
artWidth = (int)(resources.getDimension(R.dimen.artistlist_art_width) /
|
||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||
artHeight = (int)(resources.getDimension(R.dimen.artistlist_art_heigth) /
|
||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
|
||||
final View view = LayoutInflater.from(context)
|
||||
.inflate(R.layout.grid_item_artist, parent, false);
|
||||
.inflate(R.layout.grid_item_artist, parent, false);
|
||||
|
||||
// Setup View holder pattern
|
||||
ViewHolder viewHolder = new ViewHolder();
|
||||
|
|
|
@ -61,65 +61,19 @@ import de.greenrobot.event.EventBus;
|
|||
/**
|
||||
* Fragment that presents the album genres list
|
||||
*/
|
||||
public class AudioGenresListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
SearchView.OnQueryTextListener {
|
||||
public class AudioGenresListFragment extends AbstractMusicListFragment {
|
||||
private static final String TAG = LogUtils.makeLogTag(AudioGenresListFragment.class);
|
||||
|
||||
public interface OnAudioGenreSelectedListener {
|
||||
public void onAudioGenreSelected(int genreId, String genreTitle);
|
||||
}
|
||||
|
||||
// Loader IDs
|
||||
private static final int LOADER_AUDIO_GENRES = 0;
|
||||
|
||||
// The search filter to use in the loader
|
||||
private String searchFilter = null;
|
||||
|
||||
// Movies adapter
|
||||
private CursorAdapter adapter;
|
||||
|
||||
// Activity listener
|
||||
private OnAudioGenreSelectedListener listenerActivity;
|
||||
|
||||
private HostInfo hostInfo;
|
||||
private EventBus bus;
|
||||
|
||||
@InjectView(R.id.list) GridView audioGenresGridView;
|
||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
||||
@InjectView(android.R.id.empty) TextView emptyView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
bus = EventBus.getDefault();
|
||||
hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
//UIUtils.setSwipeRefreshLayoutColorScheme(swipeRefreshLayout);
|
||||
|
||||
// Pad main content view to overlap with bottom system bar
|
||||
// UIUtils.setPaddingForSystemBars(getActivity(), audioGenresGridView, false, false, true);
|
||||
// audioGenresGridView.setClipToPadding(false);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
audioGenresGridView.setEmptyView(emptyView);
|
||||
audioGenresGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
protected AdapterView.OnItemClickListener createOnItemClickListener() {
|
||||
return new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Get the movie id from the tag
|
||||
|
@ -127,14 +81,29 @@ public class AudioGenresListFragment extends Fragment
|
|||
// Notify the activity
|
||||
listenerActivity.onAudioGenreSelected(tag.genreId, tag.genreTitle);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Configure the adapter and start the loader
|
||||
adapter = new AudioGenresAdapter(getActivity());
|
||||
audioGenresGridView.setAdapter(adapter);
|
||||
getLoaderManager().initLoader(LOADER_AUDIO_GENRES, null, this);
|
||||
@Override
|
||||
protected CursorAdapter createAdapter() {
|
||||
return new AudioGenresAdapter(getActivity());
|
||||
}
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
@Override
|
||||
protected CursorLoader createCursorLoader() {
|
||||
HostInfo hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
Uri uri = MediaContract.AudioGenres.buildAudioGenresListUri(hostInfo != null ? hostInfo.getId() : -1);
|
||||
|
||||
String selection = null;
|
||||
String selectionArgs[] = null;
|
||||
String searchFilter = getSearchFilter();
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection = MediaContract.AudioGenres.TITLE + " LIKE ?";
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
AudioGenreListQuery.PROJECTION, selection, selectionArgs, AudioGenreListQuery.SORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,18 +122,6 @@ public class AudioGenresListFragment extends Fragment
|
|||
listenerActivity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
bus.register(this);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
bus.unregister(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.media_search, menu);
|
||||
|
@ -175,102 +132,6 @@ public class AudioGenresListFragment extends Fragment
|
|||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search view callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
searchFilter = newText;
|
||||
getLoaderManager().restartLoader(LOADER_AUDIO_GENRES, null, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String newText) {
|
||||
// All is handled in onQueryTextChange
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
if (hostInfo != null) {
|
||||
// Make sure we're showing the refresh
|
||||
swipeRefreshLayout.setRefreshing(true);
|
||||
// Start the syncing process
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_MUSIC, true);
|
||||
getActivity().startService(syncIntent);
|
||||
} else {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event bus post. Called when the syncing process ended
|
||||
*
|
||||
* @param event Refreshes data
|
||||
*/
|
||||
public void onEventMainThread(MediaSyncEvent event) {
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_ALL_MUSIC)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
getLoaderManager().restartLoader(LOADER_AUDIO_GENRES, null, this);
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
Uri uri = MediaContract.AudioGenres.buildAudioGenresListUri(hostInfo != null ? hostInfo.getId() : -1);
|
||||
|
||||
String selection = null;
|
||||
String selectionArgs[] = null;
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection = MediaContract.AudioGenres.TITLE + " LIKE ?";
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
AudioGenreListQuery.PROJECTION, selection, selectionArgs, AudioGenreListQuery.SORT);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
adapter.swapCursor(cursor);
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.no_genres_found_refresh));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||
adapter.swapCursor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio genres list query parameters.
|
||||
|
|
|
@ -22,15 +22,10 @@ import android.content.SharedPreferences;
|
|||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -41,7 +36,6 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
@ -55,77 +49,26 @@ import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
|||
import org.xbmc.kore.provider.MediaContract;
|
||||
import org.xbmc.kore.provider.MediaDatabase;
|
||||
import org.xbmc.kore.service.LibrarySyncService;
|
||||
import org.xbmc.kore.service.SyncUtils;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.InjectView;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Fragment that presents the movie list
|
||||
*/
|
||||
public class MovieListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
SearchView.OnQueryTextListener {
|
||||
public class MovieListFragment extends AbstractListFragment {
|
||||
private static final String TAG = LogUtils.makeLogTag(MovieListFragment.class);
|
||||
|
||||
public interface OnMovieSelectedListener {
|
||||
public void onMovieSelected(int movieId, String movieTitle);
|
||||
}
|
||||
|
||||
// Loader IDs
|
||||
private static final int LOADER_MOVIES = 0;
|
||||
|
||||
// The search filter to use in the loader
|
||||
private String searchFilter = null;
|
||||
|
||||
// Movies adapter
|
||||
private CursorAdapter adapter;
|
||||
|
||||
// Activity listener
|
||||
private OnMovieSelectedListener listenerActivity;
|
||||
|
||||
private HostManager hostManager;
|
||||
private HostInfo hostInfo;
|
||||
private EventBus bus;
|
||||
|
||||
@InjectView(R.id.list) GridView moviesGridView;
|
||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
||||
@InjectView(android.R.id.empty) TextView emptyView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
bus = EventBus.getDefault();
|
||||
hostManager = HostManager.getInstance(getActivity());
|
||||
hostInfo = hostManager.getHostInfo();
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
//UIUtils.setSwipeRefreshLayoutColorScheme(swipeRefreshLayout);
|
||||
|
||||
// Pad main content view to overlap with bottom system bar
|
||||
// UIUtils.setPaddingForSystemBars(getActivity(), moviesGridView, false, false, true);
|
||||
// moviesGridView.setClipToPadding(false);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
moviesGridView.setEmptyView(emptyView);
|
||||
moviesGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
protected AdapterView.OnItemClickListener createOnItemClickListener() {
|
||||
return new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Get the movie id from the tag
|
||||
|
@ -133,14 +76,90 @@ public class MovieListFragment extends Fragment
|
|||
// Notify the activity
|
||||
listenerActivity.onMovieSelected(tag.movieId, tag.movieTitle);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Configure the adapter and start the loader
|
||||
adapter = new MoviesAdapter(getActivity());
|
||||
moviesGridView.setAdapter(adapter);
|
||||
getLoaderManager().initLoader(LOADER_MOVIES, null, this);
|
||||
@Override
|
||||
protected CursorAdapter createAdapter() {
|
||||
return new MoviesAdapter(getActivity());
|
||||
}
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
@Override
|
||||
protected void onSwipeRefresh() {
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_MOVIES, true);
|
||||
getActivity().startService(syncIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CursorLoader createCursorLoader() {
|
||||
HostInfo hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
Uri uri = MediaContract.Movies.buildMoviesListUri(hostInfo != null? hostInfo.getId() : -1);
|
||||
|
||||
StringBuilder selection = new StringBuilder();
|
||||
String selectionArgs[] = null;
|
||||
String searchFilter = getSearchFilter();
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection.append(MediaContract.MoviesColumns.TITLE + " LIKE ?");
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
if (preferences.getBoolean(Settings.KEY_PREF_MOVIES_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_MOVIES_FILTER_HIDE_WATCHED)) {
|
||||
if (selection.length() != 0)
|
||||
selection.append(" AND ");
|
||||
selection.append(MediaContract.MoviesColumns.PLAYCOUNT)
|
||||
.append("=0");
|
||||
}
|
||||
|
||||
String sortOrderStr;
|
||||
int sortOrder = preferences.getInt(Settings.KEY_PREF_MOVIES_SORT_ORDER, Settings.DEFAULT_PREF_MOVIES_SORT_ORDER);
|
||||
if (sortOrder == Settings.SORT_BY_DATE_ADDED) {
|
||||
sortOrderStr = MovieListQuery.SORT_BY_DATE_ADDED;
|
||||
} else {
|
||||
// Sort by name
|
||||
if (preferences.getBoolean(Settings.KEY_PREF_MOVIES_IGNORE_PREFIXES, Settings.DEFAULT_PREF_MOVIES_IGNORE_PREFIXES)) {
|
||||
sortOrderStr = MovieListQuery.SORT_BY_NAME_IGNORE_ARTICLES;
|
||||
} else {
|
||||
sortOrderStr = MovieListQuery.SORT_BY_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
MovieListQuery.PROJECTION, selection.toString(), selectionArgs, sortOrderStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
||||
boolean silentSync = false;
|
||||
if (event.syncExtras != null) {
|
||||
silentSync = event.syncExtras.getBoolean(LibrarySyncService.SILENT_SYNC, false);
|
||||
}
|
||||
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_SINGLE_MOVIE) ||
|
||||
event.syncType.equals(LibrarySyncService.SYNC_ALL_MOVIES)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
refreshList();
|
||||
if (!silentSync) {
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
} else if (!silentSync) {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(LibrarySyncService librarySyncService) {
|
||||
if(SyncUtils.isLibrarySyncing(librarySyncService, HostManager.getInstance(getActivity()).getHostInfo(),
|
||||
LibrarySyncService.SYNC_ALL_MOVIES, LibrarySyncService.SYNC_SINGLE_MOVIE)) {
|
||||
showRefreshAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,18 +178,6 @@ public class MovieListFragment extends Fragment
|
|||
listenerActivity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
bus.register(this);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
bus.unregister(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.movie_list, menu);
|
||||
|
@ -213,28 +220,28 @@ public class MovieListFragment extends Fragment
|
|||
preferences.edit()
|
||||
.putBoolean(Settings.KEY_PREF_MOVIES_FILTER_HIDE_WATCHED, item.isChecked())
|
||||
.apply();
|
||||
getLoaderManager().restartLoader(LOADER_MOVIES, null, this);
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_ignore_prefixes:
|
||||
item.setChecked(!item.isChecked());
|
||||
preferences.edit()
|
||||
.putBoolean(Settings.KEY_PREF_MOVIES_IGNORE_PREFIXES, item.isChecked())
|
||||
.apply();
|
||||
getLoaderManager().restartLoader(LOADER_MOVIES, null, this);
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_sort_by_name:
|
||||
item.setChecked(true);
|
||||
preferences.edit()
|
||||
.putInt(Settings.KEY_PREF_MOVIES_SORT_ORDER, Settings.SORT_BY_NAME)
|
||||
.apply();
|
||||
getLoaderManager().restartLoader(LOADER_MOVIES, null, this);
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_sort_by_date_added:
|
||||
item.setChecked(true);
|
||||
preferences.edit()
|
||||
.putInt(Settings.KEY_PREF_MOVIES_SORT_ORDER, Settings.SORT_BY_DATE_ADDED)
|
||||
.apply();
|
||||
getLoaderManager().restartLoader(LOADER_MOVIES, null, this);
|
||||
refreshList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -243,132 +250,6 @@ public class MovieListFragment extends Fragment
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search view callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
searchFilter = newText;
|
||||
getLoaderManager().restartLoader(LOADER_MOVIES, null, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String newText) {
|
||||
// All is handled in onQueryTextChange
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onRefresh () {
|
||||
if (hostInfo != null) {
|
||||
// Make sure we're showing the refresh
|
||||
swipeRefreshLayout.setRefreshing(true);
|
||||
// Start the syncing process
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_MOVIES, true);
|
||||
getActivity().startService(syncIntent);
|
||||
|
||||
// Toast.makeText(getActivity(),
|
||||
// String.format(getString(R.string.sync_movies_for_host), hostInfo.getName()),
|
||||
// Toast.LENGTH_SHORT)
|
||||
// .show();
|
||||
} else {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event bus post. Called when the syncing process ended
|
||||
*
|
||||
* @param event Refreshes data
|
||||
*/
|
||||
public void onEventMainThread(MediaSyncEvent event) {
|
||||
boolean silentSync = false;
|
||||
if (event.syncExtras != null) {
|
||||
silentSync = event.syncExtras.getBoolean(LibrarySyncService.SILENT_SYNC, false);
|
||||
}
|
||||
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_SINGLE_MOVIE) ||
|
||||
event.syncType.equals(LibrarySyncService.SYNC_ALL_MOVIES)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
getLoaderManager().restartLoader(LOADER_MOVIES, null, this);
|
||||
if (!silentSync) {
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
} else if (!silentSync) {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
Uri uri = MediaContract.Movies.buildMoviesListUri(hostInfo != null? hostInfo.getId() : -1);
|
||||
|
||||
StringBuilder selection = new StringBuilder();
|
||||
String selectionArgs[] = null;
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection.append(MediaContract.MoviesColumns.TITLE + " LIKE ?");
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
if (preferences.getBoolean(Settings.KEY_PREF_MOVIES_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_MOVIES_FILTER_HIDE_WATCHED)) {
|
||||
if (selection.length() != 0)
|
||||
selection.append(" AND ");
|
||||
selection.append(MediaContract.MoviesColumns.PLAYCOUNT)
|
||||
.append("=0");
|
||||
}
|
||||
|
||||
String sortOrderStr;
|
||||
int sortOrder = preferences.getInt(Settings.KEY_PREF_MOVIES_SORT_ORDER, Settings.DEFAULT_PREF_MOVIES_SORT_ORDER);
|
||||
if (sortOrder == Settings.SORT_BY_DATE_ADDED) {
|
||||
sortOrderStr = MovieListQuery.SORT_BY_DATE_ADDED;
|
||||
} else {
|
||||
// Sort by name
|
||||
if (preferences.getBoolean(Settings.KEY_PREF_MOVIES_IGNORE_PREFIXES, Settings.DEFAULT_PREF_MOVIES_IGNORE_PREFIXES)) {
|
||||
sortOrderStr = MovieListQuery.SORT_BY_NAME_IGNORE_ARTICLES;
|
||||
} else {
|
||||
sortOrderStr = MovieListQuery.SORT_BY_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
MovieListQuery.PROJECTION, selection.toString(), selectionArgs, sortOrderStr);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
adapter.swapCursor(cursor);
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.no_movies_found_refresh));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||
adapter.swapCursor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Movie list query parameters.
|
||||
*/
|
||||
|
|
|
@ -15,20 +15,44 @@
|
|||
*/
|
||||
package org.xbmc.kore.ui;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.astuetz.PagerSlidingTabStrip;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.host.HostInfo;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
import org.xbmc.kore.jsonrpc.ApiException;
|
||||
import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
||||
import org.xbmc.kore.service.LibrarySyncService;
|
||||
import org.xbmc.kore.service.SyncUtils;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.TabsAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.InjectView;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Container for the various music lists
|
||||
|
@ -36,20 +60,17 @@ import butterknife.InjectView;
|
|||
public class MusicListFragment extends Fragment {
|
||||
private static final String TAG = LogUtils.makeLogTag(MusicListFragment.class);
|
||||
|
||||
private TabsAdapter tabsAdapter;
|
||||
|
||||
@InjectView(R.id.pager_tab_strip) PagerSlidingTabStrip pagerTabStrip;
|
||||
@InjectView(R.id.pager) ViewPager viewPager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_music_list, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
TabsAdapter tabsAdapter = new TabsAdapter(getActivity(), getChildFragmentManager())
|
||||
tabsAdapter = new TabsAdapter(getActivity(), getChildFragmentManager())
|
||||
.addTab(ArtistListFragment.class, getArguments(), R.string.artists, 1)
|
||||
.addTab(AlbumListFragment.class, getArguments(), R.string.albums, 2)
|
||||
.addTab(AudioGenresListFragment.class, getArguments(), R.string.genres, 3)
|
||||
|
@ -66,20 +87,4 @@ public class MusicListFragment extends Fragment {
|
|||
super.onActivityCreated(savedInstanceState);
|
||||
setHasOptionsMenu(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
// outState.putInt(TVSHOWID, tvshowId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,9 @@ import android.content.Intent;
|
|||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.SearchView;
|
||||
|
@ -39,7 +36,6 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
@ -52,74 +48,26 @@ import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
|||
import org.xbmc.kore.provider.MediaContract;
|
||||
import org.xbmc.kore.provider.MediaDatabase;
|
||||
import org.xbmc.kore.service.LibrarySyncService;
|
||||
import org.xbmc.kore.service.SyncUtils;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.InjectView;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Fragment that presents the artists list
|
||||
*/
|
||||
public class MusicVideoListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
SearchView.OnQueryTextListener {
|
||||
public class MusicVideoListFragment extends AbstractListFragment {
|
||||
private static final String TAG = LogUtils.makeLogTag(MusicVideoListFragment.class);
|
||||
|
||||
public interface OnMusicVideoSelectedListener {
|
||||
public void onMusicVideoSelected(int musicVideoId, String musicVideoTitle);
|
||||
}
|
||||
|
||||
// Loader IDs
|
||||
private static final int LOADER_MUSIC_VIDEOS = 0;
|
||||
|
||||
// The search filter to use in the loader
|
||||
private String searchFilter = null;
|
||||
|
||||
// Movies adapter
|
||||
private CursorAdapter adapter;
|
||||
|
||||
// Activity listener
|
||||
private OnMusicVideoSelectedListener listenerActivity;
|
||||
|
||||
private HostInfo hostInfo;
|
||||
private EventBus bus;
|
||||
|
||||
@InjectView(R.id.list) GridView musicVideosGridView;
|
||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
||||
@InjectView(android.R.id.empty) TextView emptyView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
bus = EventBus.getDefault();
|
||||
hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
// UIUtils.setSwipeRefreshLayoutColorScheme(swipeRefreshLayout);
|
||||
|
||||
// Pad main content view to overlap with bottom system bar
|
||||
// UIUtils.setPaddingForSystemBars(getActivity(), musicVideosGridView, false, false, true);
|
||||
// musicVideosGridView.setClipToPadding(false);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
musicVideosGridView.setEmptyView(emptyView);
|
||||
musicVideosGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
protected AdapterView.OnItemClickListener createOnItemClickListener() {
|
||||
return new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Get the movie id from the tag
|
||||
|
@ -127,14 +75,61 @@ public class MusicVideoListFragment extends Fragment
|
|||
// Notify the activity
|
||||
listenerActivity.onMusicVideoSelected(tag.musicVideoId, tag.musicVideoTitle);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Configure the adapter and start the loader
|
||||
adapter = new MusicVideosAdapter(getActivity());
|
||||
musicVideosGridView.setAdapter(adapter);
|
||||
getLoaderManager().initLoader(LOADER_MUSIC_VIDEOS, null, this);
|
||||
@Override
|
||||
protected CursorAdapter createAdapter() {
|
||||
return new MusicVideosAdapter(getActivity());
|
||||
}
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
@Override
|
||||
protected void onSwipeRefresh() {
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_MUSIC_VIDEOS, true);
|
||||
getActivity().startService(syncIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CursorLoader createCursorLoader() {
|
||||
HostInfo hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
Uri uri = MediaContract.MusicVideos.buildMusicVideosListUri(hostInfo != null ? hostInfo.getId() : -1);
|
||||
|
||||
String selection = null;
|
||||
String selectionArgs[] = null;
|
||||
String searchFilter = getSearchFilter();
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection = MediaContract.MusicVideosColumns.TITLE + " LIKE ?";
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
MusicVideosListQuery.PROJECTION, selection, selectionArgs, MusicVideosListQuery.SORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_ALL_MUSIC_VIDEOS)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
refreshList();
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(LibrarySyncService librarySyncService) {
|
||||
if(SyncUtils.isLibrarySyncing(librarySyncService, HostManager.getInstance(getActivity()).getHostInfo(),
|
||||
LibrarySyncService.SYNC_ALL_MUSIC_VIDEOS)) {
|
||||
showRefreshAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,18 +148,6 @@ public class MusicVideoListFragment extends Fragment
|
|||
listenerActivity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
bus.register(this);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
bus.unregister(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.media_search, menu);
|
||||
|
@ -175,103 +158,6 @@ public class MusicVideoListFragment extends Fragment
|
|||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search view callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
searchFilter = newText;
|
||||
getLoaderManager().restartLoader(LOADER_MUSIC_VIDEOS, null, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String newText) {
|
||||
// All is handled in onQueryTextChange
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
if (hostInfo != null) {
|
||||
// Make sure we're showing the refresh
|
||||
swipeRefreshLayout.setRefreshing(true);
|
||||
// Start the syncing process
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_MUSIC_VIDEOS, true);
|
||||
getActivity().startService(syncIntent);
|
||||
} else {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event bus post. Called when the syncing process ended
|
||||
*
|
||||
* @param event Refreshes data
|
||||
*/
|
||||
public void onEventMainThread(MediaSyncEvent event) {
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_ALL_MUSIC_VIDEOS)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
getLoaderManager().restartLoader(LOADER_MUSIC_VIDEOS, null, this);
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
Uri uri = MediaContract.MusicVideos.buildMusicVideosListUri(hostInfo != null ? hostInfo.getId() : -1);
|
||||
|
||||
String selection = null;
|
||||
String selectionArgs[] = null;
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection = MediaContract.MusicVideosColumns.TITLE + " LIKE ?";
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
MusicVideosListQuery.PROJECTION, selection, selectionArgs, MusicVideosListQuery.SORT);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
adapter.swapCursor(cursor);
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.no_music_videos_found_refresh));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||
adapter.swapCursor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Videos list query parameters.
|
||||
*/
|
||||
|
|
|
@ -55,6 +55,7 @@ import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
|||
import org.xbmc.kore.provider.MediaContract;
|
||||
import org.xbmc.kore.provider.MediaDatabase;
|
||||
import org.xbmc.kore.service.LibrarySyncService;
|
||||
import org.xbmc.kore.service.SyncUtils;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
|
@ -65,66 +66,19 @@ import de.greenrobot.event.EventBus;
|
|||
/**
|
||||
* Fragment that presents the tv show list
|
||||
*/
|
||||
public class TVShowListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
SearchView.OnQueryTextListener {
|
||||
public class TVShowListFragment extends AbstractListFragment {
|
||||
private static final String TAG = LogUtils.makeLogTag(TVShowListFragment.class);
|
||||
|
||||
public interface OnTVShowSelectedListener {
|
||||
public void onTVShowSelected(int tvshowId, String tvshowTitle);
|
||||
}
|
||||
|
||||
// Loader IDs
|
||||
private static final int LOADER_TVSHOWS = 0;
|
||||
|
||||
// The search filter to use in the loader
|
||||
private String searchFilter = null;
|
||||
|
||||
// Movies adapter
|
||||
private CursorAdapter adapter;
|
||||
|
||||
// Activity listener
|
||||
private OnTVShowSelectedListener listenerActivity;
|
||||
|
||||
private HostManager hostManager;
|
||||
private HostInfo hostInfo;
|
||||
private EventBus bus;
|
||||
|
||||
@InjectView(R.id.list) GridView tvshowsGridView;
|
||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
||||
@InjectView(android.R.id.empty) TextView emptyView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
bus = EventBus.getDefault();
|
||||
hostManager = HostManager.getInstance(getActivity());
|
||||
hostInfo = hostManager.getHostInfo();
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
//UIUtils.setSwipeRefreshLayoutColorScheme(swipeRefreshLayout);
|
||||
|
||||
// Pad main content view to overlap with bottom system bar
|
||||
// UIUtils.setPaddingForSystemBars(getActivity(), moviesGridView, false, false, true);
|
||||
// moviesGridView.setClipToPadding(false);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
tvshowsGridView.setEmptyView(emptyView);
|
||||
tvshowsGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
protected AdapterView.OnItemClickListener createOnItemClickListener() {
|
||||
return new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Get the movie id from the tag
|
||||
|
@ -132,14 +86,94 @@ public class TVShowListFragment extends Fragment
|
|||
// Notify the activity
|
||||
listenerActivity.onTVShowSelected(tag.tvshowId, tag.tvshowTitle);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Configure the adapter and start the loader
|
||||
adapter = new TVShowsAdapter(getActivity());
|
||||
tvshowsGridView.setAdapter(adapter);
|
||||
getLoaderManager().initLoader(LOADER_TVSHOWS, null, this);
|
||||
@Override
|
||||
protected CursorAdapter createAdapter() {
|
||||
return new TVShowsAdapter(getActivity());
|
||||
}
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
@Override
|
||||
protected void onSwipeRefresh() {
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_TVSHOWS, true);
|
||||
getActivity().startService(syncIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CursorLoader createCursorLoader() {
|
||||
HostInfo hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||
Uri uri = MediaContract.TVShows.buildTVShowsListUri(hostInfo != null ? hostInfo.getId() : -1);
|
||||
|
||||
StringBuilder selection = new StringBuilder();
|
||||
String selectionArgs[] = null;
|
||||
String searchFilter = getSearchFilter();
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection.append(MediaContract.TVShowsColumns.TITLE + " LIKE ?");
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
// Filters
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
if (preferences.getBoolean(Settings.KEY_PREF_TVSHOWS_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_TVSHOWS_FILTER_HIDE_WATCHED)) {
|
||||
if (selection.length() != 0)
|
||||
selection.append(" AND ");
|
||||
selection.append(MediaContract.TVShowsColumns.WATCHEDEPISODES)
|
||||
.append("!=")
|
||||
.append(MediaContract.TVShowsColumns.EPISODE);
|
||||
}
|
||||
|
||||
String sortOrderStr;
|
||||
int sortOrder = preferences.getInt(Settings.KEY_PREF_TVSHOWS_SORT_ORDER, Settings.DEFAULT_PREF_TVSHOWS_SORT_ORDER);
|
||||
if (sortOrder == Settings.SORT_BY_DATE_ADDED) {
|
||||
sortOrderStr = TVShowListQuery.SORT_BY_DATE_ADDED;
|
||||
} else {
|
||||
// Sort by name
|
||||
if (preferences.getBoolean(Settings.KEY_PREF_TVSHOWS_IGNORE_PREFIXES, Settings.DEFAULT_PREF_TVSHOWS_IGNORE_PREFIXES)) {
|
||||
sortOrderStr = TVShowListQuery.SORT_BY_NAME_IGNORE_ARTICLES;
|
||||
} else {
|
||||
sortOrderStr = TVShowListQuery.SORT_BY_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
TVShowListQuery.PROJECTION, selection.toString(),
|
||||
selectionArgs, sortOrderStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
||||
boolean silentSync = false;
|
||||
if (event.syncExtras != null) {
|
||||
silentSync = event.syncExtras.getBoolean(LibrarySyncService.SILENT_SYNC, false);
|
||||
}
|
||||
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_SINGLE_TVSHOW) ||
|
||||
event.syncType.equals(LibrarySyncService.SYNC_ALL_TVSHOWS)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
refreshList();
|
||||
if (!silentSync) {
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
} else if (!silentSync) {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(LibrarySyncService librarySyncService) {
|
||||
if(SyncUtils.isLibrarySyncing(librarySyncService, HostManager.getInstance(getActivity()).getHostInfo(),
|
||||
LibrarySyncService.SYNC_ALL_TVSHOWS, LibrarySyncService.SYNC_SINGLE_TVSHOW)) {
|
||||
showRefreshAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -158,18 +192,6 @@ public class TVShowListFragment extends Fragment
|
|||
listenerActivity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
bus.register(this);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
bus.unregister(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.tvshow_list, menu);
|
||||
|
@ -210,28 +232,28 @@ public class TVShowListFragment extends Fragment
|
|||
preferences.edit()
|
||||
.putBoolean(Settings.KEY_PREF_TVSHOWS_FILTER_HIDE_WATCHED, item.isChecked())
|
||||
.apply();
|
||||
getLoaderManager().restartLoader(LOADER_TVSHOWS, null, this);
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_ignore_prefixes:
|
||||
item.setChecked(!item.isChecked());
|
||||
preferences.edit()
|
||||
.putBoolean(Settings.KEY_PREF_TVSHOWS_IGNORE_PREFIXES, item.isChecked())
|
||||
.apply();
|
||||
getLoaderManager().restartLoader(LOADER_TVSHOWS, null, this);
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_sort_by_name:
|
||||
item.setChecked(true);
|
||||
preferences.edit()
|
||||
.putInt(Settings.KEY_PREF_TVSHOWS_SORT_ORDER, Settings.SORT_BY_NAME)
|
||||
.apply();
|
||||
getLoaderManager().restartLoader(LOADER_TVSHOWS, null, this);
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_sort_by_date_added:
|
||||
item.setChecked(true);
|
||||
preferences.edit()
|
||||
.putInt(Settings.KEY_PREF_TVSHOWS_SORT_ORDER, Settings.SORT_BY_DATE_ADDED)
|
||||
.apply();
|
||||
getLoaderManager().restartLoader(LOADER_TVSHOWS, null, this);
|
||||
refreshList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -240,132 +262,6 @@ public class TVShowListFragment extends Fragment
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search view callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
searchFilter = newText;
|
||||
getLoaderManager().restartLoader(LOADER_TVSHOWS, null, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String newText) {
|
||||
// All is handled in onQueryTextChange
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
if (hostInfo != null) {
|
||||
LogUtils.LOGD(TAG, "Starting onRefresh");
|
||||
// Make sure we're showing the refresh
|
||||
swipeRefreshLayout.setRefreshing(true);
|
||||
// Start the syncing process
|
||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||
syncIntent.putExtra(LibrarySyncService.SYNC_ALL_TVSHOWS, true);
|
||||
getActivity().startService(syncIntent);
|
||||
} else {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event bus post. Called when the syncing process ended
|
||||
*
|
||||
* @param event Refreshes data
|
||||
*/
|
||||
public void onEventMainThread(MediaSyncEvent event) {
|
||||
boolean silentSync = false;
|
||||
if (event.syncExtras != null) {
|
||||
silentSync = event.syncExtras.getBoolean(LibrarySyncService.SILENT_SYNC, false);
|
||||
}
|
||||
|
||||
if (event.syncType.equals(LibrarySyncService.SYNC_SINGLE_TVSHOW) ||
|
||||
event.syncType.equals(LibrarySyncService.SYNC_ALL_TVSHOWS)) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
getLoaderManager().restartLoader(LOADER_TVSHOWS, null, this);
|
||||
if (!silentSync) {
|
||||
Toast.makeText(getActivity(), R.string.sync_successful, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
} else if (!silentSync) {
|
||||
String msg = (event.errorCode == ApiException.API_ERROR) ?
|
||||
String.format(getString(R.string.error_while_syncing), event.errorMessage) :
|
||||
getString(R.string.unable_to_connect_to_xbmc);
|
||||
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
Uri uri = MediaContract.TVShows.buildTVShowsListUri(hostInfo != null ? hostInfo.getId() : -1);
|
||||
|
||||
StringBuilder selection = new StringBuilder();
|
||||
String selectionArgs[] = null;
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
selection.append(MediaContract.TVShowsColumns.TITLE + " LIKE ?");
|
||||
selectionArgs = new String[] {"%" + searchFilter + "%"};
|
||||
}
|
||||
|
||||
// Filters
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
if (preferences.getBoolean(Settings.KEY_PREF_TVSHOWS_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_TVSHOWS_FILTER_HIDE_WATCHED)) {
|
||||
if (selection.length() != 0)
|
||||
selection.append(" AND ");
|
||||
selection.append(MediaContract.TVShowsColumns.WATCHEDEPISODES)
|
||||
.append("!=")
|
||||
.append(MediaContract.TVShowsColumns.EPISODE);
|
||||
}
|
||||
|
||||
String sortOrderStr;
|
||||
int sortOrder = preferences.getInt(Settings.KEY_PREF_TVSHOWS_SORT_ORDER, Settings.DEFAULT_PREF_TVSHOWS_SORT_ORDER);
|
||||
if (sortOrder == Settings.SORT_BY_DATE_ADDED) {
|
||||
sortOrderStr = TVShowListQuery.SORT_BY_DATE_ADDED;
|
||||
} else {
|
||||
// Sort by name
|
||||
if (preferences.getBoolean(Settings.KEY_PREF_TVSHOWS_IGNORE_PREFIXES, Settings.DEFAULT_PREF_TVSHOWS_IGNORE_PREFIXES)) {
|
||||
sortOrderStr = TVShowListQuery.SORT_BY_NAME_IGNORE_ARTICLES;
|
||||
} else {
|
||||
sortOrderStr = TVShowListQuery.SORT_BY_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new CursorLoader(getActivity(), uri,
|
||||
TVShowListQuery.PROJECTION, selection.toString(),
|
||||
selectionArgs, sortOrderStr);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
adapter.swapCursor(cursor);
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.no_tvshows_found_refresh));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||
adapter.swapCursor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* TVShow list query parameters.
|
||||
*/
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.content.res.Resources;
|
|||
import android.content.res.TypedArray;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
|
@ -229,6 +229,7 @@
|
|||
<string name="no_movies_found_refresh">No movies found\n\nSwipe down to refresh</string>
|
||||
<string name="no_tvshows_found_refresh">No TV Shows found\n\nSwipe down to refresh</string>
|
||||
<string name="no_episodes_found">No episodes found</string>
|
||||
<string name="swipe_down_to_refresh">Swipe down to refresh</string>
|
||||
<string name="no_artists_found_refresh">No artists found\n\nSwipe down to refresh</string>
|
||||
<string name="no_albums_found_refresh">No albums found\n\nSwipe down to refresh</string>
|
||||
<string name="no_genres_found_refresh">No genres found\n\nSwipe down to refresh</string>
|
||||
|
|
Loading…
Reference in New Issue