diff --git a/app/src/main/java/com/cappielloantonio/play/interfaces/ScanCallback.java b/app/src/main/java/com/cappielloantonio/play/interfaces/ScanCallback.java new file mode 100644 index 00000000..0b783b74 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/interfaces/ScanCallback.java @@ -0,0 +1,8 @@ +package com.cappielloantonio.play.interfaces; + +public interface ScanCallback { + + void onError(Exception exception); + + void onSuccess(boolean isScanning, long count); +} diff --git a/app/src/main/java/com/cappielloantonio/play/repository/ScanRepository.java b/app/src/main/java/com/cappielloantonio/play/repository/ScanRepository.java new file mode 100644 index 00000000..e0e864a6 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/repository/ScanRepository.java @@ -0,0 +1,60 @@ +package com.cappielloantonio.play.repository; + +import android.app.Application; + +import com.cappielloantonio.play.App; +import com.cappielloantonio.play.interfaces.ScanCallback; +import com.cappielloantonio.play.interfaces.SystemCallback; +import com.cappielloantonio.play.subsonic.models.ResponseStatus; +import com.cappielloantonio.play.subsonic.models.SubsonicResponse; + +import retrofit2.Call; +import retrofit2.Callback; + +public class ScanRepository { + private static final String TAG = "SongRepository"; + + private Application application; + + public ScanRepository(Application application) { + this.application = application; + } + + public void startScan(ScanCallback callback) { + App.getSubsonicClientInstance(application, false) + .getMediaLibraryScanningClient() + .startScan() + .enqueue(new Callback() { + @Override + public void onResponse(Call call, retrofit2.Response response) { + if (response.body().getScanStatus() != null) { + callback.onSuccess(response.body().getScanStatus().isScanning(), response.body().getScanStatus().getCount()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + callback.onError(new Exception(t.getMessage())); + } + }); + } + + public void getScanStatus(ScanCallback callback) { + App.getSubsonicClientInstance(application, false) + .getMediaLibraryScanningClient() + .startScan() + .enqueue(new Callback() { + @Override + public void onResponse(Call call, retrofit2.Response response) { + if (response.body().getScanStatus() != null) { + callback.onSuccess(response.body().getScanStatus().isScanning(), response.body().getScanStatus().getCount()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + callback.onError(new Exception(t.getMessage())); + } + }); + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/repository/SystemRepository.java b/app/src/main/java/com/cappielloantonio/play/repository/SystemRepository.java index eb60ff3f..b4366df3 100644 --- a/app/src/main/java/com/cappielloantonio/play/repository/SystemRepository.java +++ b/app/src/main/java/com/cappielloantonio/play/repository/SystemRepository.java @@ -20,7 +20,8 @@ public class SystemRepository { } public void checkUserCredential(SystemCallback callback) { - App.getSubsonicClientInstance(application, false).getSystemClient() + App.getSubsonicClientInstance(application, false) + .getSystemClient() .ping() .enqueue(new Callback() { @Override diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/Subsonic.java b/app/src/main/java/com/cappielloantonio/play/subsonic/Subsonic.java index 309c1df9..69f5fa5f 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/Subsonic.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/Subsonic.java @@ -3,6 +3,7 @@ package com.cappielloantonio.play.subsonic; import com.cappielloantonio.play.subsonic.api.albumsonglist.AlbumSongListClient; import com.cappielloantonio.play.subsonic.api.browsing.BrowsingClient; import com.cappielloantonio.play.subsonic.api.mediaannotation.MediaAnnotationClient; +import com.cappielloantonio.play.subsonic.api.medialibraryscanning.MediaLibraryScanningClient; import com.cappielloantonio.play.subsonic.api.mediaretrieval.MediaRetrievalClient; import com.cappielloantonio.play.subsonic.api.playlist.PlaylistClient; import com.cappielloantonio.play.subsonic.api.searching.SearchingClient; @@ -25,6 +26,7 @@ public class Subsonic { private SearchingClient searchingClient; private AlbumSongListClient albumSongListClient; private MediaAnnotationClient mediaAnnotationClient; + private MediaLibraryScanningClient mediaLibraryScanningClient; public Subsonic(SubsonicPreferences preferences) { this.preferences = preferences; @@ -83,6 +85,13 @@ public class Subsonic { return mediaAnnotationClient; } + public MediaLibraryScanningClient getMediaLibraryScanningClient() { + if (mediaLibraryScanningClient == null) { + mediaLibraryScanningClient = new MediaLibraryScanningClient(this); + } + return mediaLibraryScanningClient; + } + public String getUrl() { String url = preferences.getServerUrl() + "/rest/"; diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/api/medialibraryscanning/MediaLibraryScanningClient.java b/app/src/main/java/com/cappielloantonio/play/subsonic/api/medialibraryscanning/MediaLibraryScanningClient.java new file mode 100644 index 00000000..d9c69826 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/api/medialibraryscanning/MediaLibraryScanningClient.java @@ -0,0 +1,56 @@ +package com.cappielloantonio.play.subsonic.api.medialibraryscanning; + +import android.util.Log; + +import com.cappielloantonio.play.subsonic.Subsonic; +import com.cappielloantonio.play.subsonic.api.system.SystemService; +import com.cappielloantonio.play.subsonic.models.SubsonicResponse; +import com.tickaroo.tikxml.retrofit.TikXmlConverterFactory; + +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Call; +import retrofit2.Retrofit; + +public class MediaLibraryScanningClient { + private static final String TAG = "SystemClient"; + + private Subsonic subsonic; + private Retrofit retrofit; + private MediaLibraryScanningService mediaLibraryScanningService; + + public MediaLibraryScanningClient(Subsonic subsonic) { + this.subsonic = subsonic; + + this.retrofit = new Retrofit.Builder() + .baseUrl(subsonic.getUrl()) + .addConverterFactory(TikXmlConverterFactory.create()) + .client(getOkHttpClient()) + .build(); + + this.mediaLibraryScanningService = retrofit.create(MediaLibraryScanningService.class); + } + + public Call startScan() { + Log.d(TAG, "startScan()"); + return mediaLibraryScanningService.startScan(subsonic.getParams()); + } + + public Call getScanStatus() { + Log.d(TAG, "getScanStatus()"); + return mediaLibraryScanningService.getScanStatus(subsonic.getParams()); + } + + private OkHttpClient getOkHttpClient() { + return new OkHttpClient.Builder() + .addInterceptor(getHttpLoggingInterceptor()) + .build(); + } + + private HttpLoggingInterceptor getHttpLoggingInterceptor() { + HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + + return loggingInterceptor; + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/api/medialibraryscanning/MediaLibraryScanningService.java b/app/src/main/java/com/cappielloantonio/play/subsonic/api/medialibraryscanning/MediaLibraryScanningService.java new file mode 100644 index 00000000..207c538e --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/api/medialibraryscanning/MediaLibraryScanningService.java @@ -0,0 +1,17 @@ +package com.cappielloantonio.play.subsonic.api.medialibraryscanning; + +import com.cappielloantonio.play.subsonic.models.SubsonicResponse; + +import java.util.Map; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.QueryMap; + +public interface MediaLibraryScanningService { + @GET("startScan") + Call startScan(@QueryMap Map params); + + @GET("getScanStatus") + Call getScanStatus(@QueryMap Map params); +} diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/models/ScanStatus.java b/app/src/main/java/com/cappielloantonio/play/subsonic/models/ScanStatus.java index 5a828624..06c82e9f 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/models/ScanStatus.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/models/ScanStatus.java @@ -1,39 +1,27 @@ package com.cappielloantonio.play.subsonic.models; -public class ScanStatus { - protected boolean scanning; - protected Long count; +import com.tickaroo.tikxml.annotation.Attribute; +import com.tickaroo.tikxml.annotation.Xml; - /** - * Gets the value of the scanning property. - */ +@Xml +public class ScanStatus { + @Attribute + protected boolean scanning; + @Attribute + protected Long count; + public boolean isScanning() { return scanning; } - /** - * Sets the value of the scanning property. - */ public void setScanning(boolean value) { this.scanning = value; } - /** - * Gets the value of the count property. - * - * @return possible object is - * {@link Long } - */ public Long getCount() { return count; } - /** - * Sets the value of the count property. - * - * @param value allowed object is - * {@link Long } - */ public void setCount(Long value) { this.count = value; } diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/models/SubsonicResponse.java b/app/src/main/java/com/cappielloantonio/play/subsonic/models/SubsonicResponse.java index 8f3a8a85..a496a188 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/models/SubsonicResponse.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/models/SubsonicResponse.java @@ -9,6 +9,7 @@ import com.tickaroo.tikxml.annotation.Xml; public class SubsonicResponse { @Element private Error error; + @Element(name = "scanStatus") private ScanStatus scanStatus; @Element(name = "topSongs") private TopSongs topSongs; diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SettingsFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SettingsFragment.java index ca15d4db..f41418ba 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SettingsFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SettingsFragment.java @@ -4,7 +4,9 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; +import androidx.lifecycle.ViewModelProvider; import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; @@ -12,20 +14,25 @@ import androidx.preference.PreferenceFragmentCompat; import com.cappielloantonio.play.App; import com.cappielloantonio.play.R; import com.cappielloantonio.play.helper.ThemeHelper; +import com.cappielloantonio.play.interfaces.ScanCallback; import com.cappielloantonio.play.repository.QueueRepository; import com.cappielloantonio.play.ui.activity.MainActivity; import com.cappielloantonio.play.util.PreferenceUtil; +import com.cappielloantonio.play.viewmodel.LibraryViewModel; +import com.cappielloantonio.play.viewmodel.SettingViewModel; public class SettingsFragment extends PreferenceFragmentCompat { private static final String TAG = "SettingsFragment"; private MainActivity activity; + private SettingViewModel settingViewModel; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { activity = (MainActivity) getActivity(); View view = super.onCreateView(inflater, container, savedInstanceState); + settingViewModel = new ViewModelProvider(requireActivity()).get(SettingViewModel.class); if (view != null) { getListView().setPadding(0, 0, 0, (int) getResources().getDimension(R.dimen.global_padding_bottom)); @@ -56,6 +63,21 @@ public class SettingsFragment extends PreferenceFragmentCompat { return true; }); + + findPreference("scan_library").setOnPreferenceClickListener(preference -> { + settingViewModel.launchScan(new ScanCallback() { + @Override + public void onError(Exception exception) { + Toast.makeText(requireContext(), exception.getMessage(), Toast.LENGTH_SHORT).show(); + } + + @Override + public void onSuccess(boolean isScanning, long count) { + Toast.makeText(requireContext(), "Scanning: counting " + count + " elements", Toast.LENGTH_SHORT).show(); + } + }); + return true; + }); } @Override diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/SettingViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/SettingViewModel.java new file mode 100644 index 00000000..e28be490 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/SettingViewModel.java @@ -0,0 +1,51 @@ +package com.cappielloantonio.play.viewmodel; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; + +import com.cappielloantonio.play.interfaces.ScanCallback; +import com.cappielloantonio.play.repository.QueueRepository; +import com.cappielloantonio.play.repository.ScanRepository; +import com.cappielloantonio.play.repository.SongRepository; + +public class SettingViewModel extends AndroidViewModel { + private static final String TAG = "SettingViewModel"; + + private ScanRepository scanRepository; + + public SettingViewModel(@NonNull Application application) { + super(application); + + scanRepository = new ScanRepository(application); + } + + public void launchScan(ScanCallback callback) { + scanRepository.startScan(new ScanCallback() { + @Override + public void onError(Exception exception) { + callback.onError(exception); + } + + @Override + public void onSuccess(boolean isScanning, long count) { + callback.onSuccess(isScanning, count); + } + }); + } + + public void getScanStatus(ScanCallback callback) { + scanRepository.getScanStatus(new ScanCallback() { + @Override + public void onError(Exception exception) { + callback.onError(exception); + } + + @Override + public void onSuccess(boolean isScanning, long count) { + callback.onSuccess(isScanning, count); + } + }); + } +} diff --git a/app/src/main/res/xml/global_preferences.xml b/app/src/main/res/xml/global_preferences.xml index 73535475..bb16029c 100644 --- a/app/src/main/res/xml/global_preferences.xml +++ b/app/src/main/res/xml/global_preferences.xml @@ -28,6 +28,10 @@ app:title="@string/theme_selection" app:useSimpleSummaryProvider="true" /> + +