mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-01-30 22:32:07 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24747207b8 | ||
|
|
e762c91ff3 | ||
|
|
0fe4636fe1 | ||
|
|
8ab011781e | ||
|
|
7f820bd5a6 | ||
|
|
26b8f3f65c | ||
|
|
f172a00fb7 | ||
|
|
077d22167c | ||
|
|
ebd1582d1c | ||
|
|
5e486d4794 |
@@ -28,8 +28,8 @@ android {
|
||||
tempo {
|
||||
dimension "default"
|
||||
applicationId 'com.cappielloantonio.tempo'
|
||||
versionCode 21
|
||||
versionName '3.5.6'
|
||||
versionCode 22
|
||||
versionName '3.5.7'
|
||||
}
|
||||
|
||||
notquitemy {
|
||||
|
||||
@@ -30,4 +30,6 @@ public interface ClickCallback {
|
||||
default void onMusicDirectoryClick(Bundle bundle) {}
|
||||
default void onMusicIndexClick(Bundle bundle) {}
|
||||
default void onDownloadGroupLongClick(Bundle bundle) {}
|
||||
default void onShareClick(Bundle bundle) {}
|
||||
default void onShareLongClick(Bundle bundle) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.cappielloantonio.tempo.repository;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class SharingRepository {
|
||||
public MutableLiveData<List<Share>> getShares() {
|
||||
MutableLiveData<List<Share>> shares = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSharingClient()
|
||||
.getShares()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getShares() != null && response.body().getSubsonicResponse().getShares().getShares() != null) {
|
||||
shares.setValue(response.body().getSubsonicResponse().getShares().getShares());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return shares;
|
||||
}
|
||||
|
||||
public MutableLiveData<Share> createShare(String id, String description, Long expires) {
|
||||
MutableLiveData<Share> share = new MutableLiveData<>();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSharingClient()
|
||||
.createShare(id, description, expires)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getShares() != null && response.body().getSubsonicResponse().getShares().getShares() != null && response.body().getSubsonicResponse().getShares().getShares().get(0) != null) {
|
||||
share.setValue(response.body().getSubsonicResponse().getShares().getShares().get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return share;
|
||||
}
|
||||
|
||||
public void updateShare(String id, String description, Long expires) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSharingClient()
|
||||
.updateShare(id, description, expires)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteShare(String id) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSharingClient()
|
||||
.deleteShare(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import com.cappielloantonio.tempo.subsonic.api.mediaretrieval.MediaRetrievalClie
|
||||
import com.cappielloantonio.tempo.subsonic.api.playlist.PlaylistClient;
|
||||
import com.cappielloantonio.tempo.subsonic.api.podcast.PodcastClient;
|
||||
import com.cappielloantonio.tempo.subsonic.api.searching.SearchingClient;
|
||||
import com.cappielloantonio.tempo.subsonic.api.sharing.SharingClient;
|
||||
import com.cappielloantonio.tempo.subsonic.api.system.SystemClient;
|
||||
import com.cappielloantonio.tempo.subsonic.base.Version;
|
||||
|
||||
@@ -33,6 +34,7 @@ public class Subsonic {
|
||||
private MediaLibraryScanningClient mediaLibraryScanningClient;
|
||||
private BookmarksClient bookmarksClient;
|
||||
private InternetRadioClient internetRadioClient;
|
||||
private SharingClient sharingClient;
|
||||
|
||||
public Subsonic(SubsonicPreferences preferences) {
|
||||
this.preferences = preferences;
|
||||
@@ -119,6 +121,13 @@ public class Subsonic {
|
||||
return internetRadioClient;
|
||||
}
|
||||
|
||||
public SharingClient getSharingClient() {
|
||||
if (sharingClient == null) {
|
||||
sharingClient = new SharingClient(this);
|
||||
}
|
||||
return sharingClient;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
String url = preferences.getServerUrl() + "/rest/";
|
||||
return url.replace("//rest", "/rest");
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.cappielloantonio.tempo.subsonic.api.sharing;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.cappielloantonio.tempo.subsonic.RetrofitClient;
|
||||
import com.cappielloantonio.tempo.subsonic.Subsonic;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
|
||||
import retrofit2.Call;
|
||||
|
||||
public class SharingClient {
|
||||
private static final String TAG = "BrowsingClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final SharingService sharingService;
|
||||
|
||||
public SharingClient(Subsonic subsonic) {
|
||||
this.subsonic = subsonic;
|
||||
this.sharingService = new RetrofitClient(subsonic).getRetrofit().create(SharingService.class);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> getShares() {
|
||||
Log.d(TAG, "getShares()");
|
||||
return sharingService.getShares(subsonic.getParams());
|
||||
}
|
||||
|
||||
public Call<ApiResponse> createShare(String id, String description, Long expires) {
|
||||
Log.d(TAG, "createShare()");
|
||||
return sharingService.createShare(subsonic.getParams(), id, description, expires);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> updateShare(String id, String description, Long expires) {
|
||||
Log.d(TAG, "updateShare()");
|
||||
return sharingService.updateShare(subsonic.getParams(), id, description, expires);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> deleteShare(String id) {
|
||||
Log.d(TAG, "deleteShare()");
|
||||
return sharingService.deleteShare(subsonic.getParams(), id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.cappielloantonio.tempo.subsonic.api.sharing;
|
||||
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
import retrofit2.http.QueryMap;
|
||||
|
||||
public interface SharingService {
|
||||
@GET("getShares")
|
||||
Call<ApiResponse> getShares(@QueryMap Map<String, String> params);
|
||||
|
||||
@GET("createShare")
|
||||
Call<ApiResponse> createShare(@QueryMap Map<String, String> params, @Query("id") String id, @Query("description") String description, @Query("expires") Long expires);
|
||||
|
||||
@GET("updateShare")
|
||||
Call<ApiResponse> updateShare(@QueryMap Map<String, String> params, @Query("id") String id, @Query("description") String description, @Query("expires") Long expires);
|
||||
|
||||
@GET("deleteShare")
|
||||
Call<ApiResponse> deleteShare(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
package com.cappielloantonio.tempo.subsonic.models
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@Keep
|
||||
class Share {
|
||||
@Parcelize
|
||||
class Share : Parcelable {
|
||||
@SerializedName("entry")
|
||||
var entries: List<Child>? = null
|
||||
var id: String? = null
|
||||
var url: String? = null
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.cappielloantonio.tempo.subsonic.models
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
class Shares {
|
||||
@SerializedName("share")
|
||||
var shares: List<Share>? = null
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.cappielloantonio.tempo.ui.adapter;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.ItemHorizontalShareBinding;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ShareHorizontalAdapter extends RecyclerView.Adapter<ShareHorizontalAdapter.ViewHolder> {
|
||||
private final ClickCallback click;
|
||||
|
||||
private List<Share> shares;
|
||||
|
||||
public ShareHorizontalAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.shares = Collections.emptyList();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemHorizontalShareBinding view = ItemHorizontalShareBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
Share share = shares.get(position);
|
||||
|
||||
holder.item.shareTitleTextView.setText(MusicUtil.getReadableString(share.getDescription()));
|
||||
holder.item.shareSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.share_subtitle_item, UIUtil.getReadableDate(share.getExpires())));
|
||||
|
||||
if (share.getEntries() != null && !share.getEntries().isEmpty()) CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), share.getEntries().get(0).getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||
.build()
|
||||
.into(holder.item.shareCoverImageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return shares.size();
|
||||
}
|
||||
|
||||
public void setItems(List<Share> shares) {
|
||||
this.shares = shares;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public Share getItem(int id) {
|
||||
return shares.get(id);
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ItemHorizontalShareBinding item;
|
||||
|
||||
ViewHolder(ItemHorizontalShareBinding item) {
|
||||
super(item.getRoot());
|
||||
|
||||
this.item = item;
|
||||
|
||||
item.shareTitleTextView.setSelected(true);
|
||||
item.shareSubtitleTextView.setSelected(true);
|
||||
|
||||
itemView.setOnClickListener(v -> onClick());
|
||||
itemView.setOnLongClickListener(v -> onLongClick());
|
||||
|
||||
item.shareButton.setOnClickListener(v -> onLongClick());
|
||||
}
|
||||
|
||||
private void onClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.SHARE_OBJECT, shares.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onShareClick(bundle);
|
||||
}
|
||||
|
||||
private boolean onLongClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.SHARE_OBJECT, shares.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onShareLongClick(bundle);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,12 @@ package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
@@ -18,6 +22,7 @@ import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
|
||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogSongHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlaylistEditorViewModel;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -99,6 +104,16 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
playlistEditorViewModel.deletePlaylist();
|
||||
dialogDismiss();
|
||||
});
|
||||
|
||||
bind.playlistShareButton.setOnClickListener(view -> {
|
||||
playlistEditorViewModel.sharePlaylist().observe(requireActivity(), sharedPlaylist -> {
|
||||
ClipboardManager clipboardManager = (ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clipData = ClipData.newPlainText(getString(R.string.app_name), sharedPlaylist.getUrl());
|
||||
clipboardManager.setPrimaryClip(clipData);
|
||||
});
|
||||
});
|
||||
|
||||
bind.playlistShareButton.setVisibility(Preferences.isSharingEnabled() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void initSongsView() {
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogShareUpdateBinding;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.ShareBottomSheetViewModel;
|
||||
import com.google.android.material.datepicker.CalendarConstraints;
|
||||
import com.google.android.material.datepicker.DateValidatorPointForward;
|
||||
import com.google.android.material.datepicker.MaterialDatePicker;
|
||||
import com.google.android.material.datepicker.MaterialPickerOnPositiveButtonClickListener;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ShareUpdateDialog extends DialogFragment {
|
||||
private static final String TAG = "ShareUpdateDialog";
|
||||
|
||||
private DialogShareUpdateBinding bind;
|
||||
private HomeViewModel homeViewModel;
|
||||
private ShareBottomSheetViewModel shareBottomSheetViewModel;
|
||||
|
||||
private MaterialDatePicker<Long> datePicker;
|
||||
|
||||
private String descriptionTextView;
|
||||
private String expirationTextView;
|
||||
private long expiration;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
homeViewModel = new ViewModelProvider(requireActivity()).get(HomeViewModel.class);
|
||||
shareBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(ShareBottomSheetViewModel.class);
|
||||
|
||||
bind = DialogShareUpdateBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
.setTitle(R.string.share_update_dialog_title)
|
||||
.setPositiveButton(R.string.share_update_dialog_positive_button, (dialog, id) -> {
|
||||
})
|
||||
.setNegativeButton(R.string.share_update_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
setShareInfo();
|
||||
setShareCalendar();
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
bind = null;
|
||||
}
|
||||
|
||||
private void setShareInfo() {
|
||||
if (shareBottomSheetViewModel.getShare() != null) {
|
||||
bind.shareDescriptionTextView.setText(shareBottomSheetViewModel.getShare().getDescription());
|
||||
// bind.shareExpirationTextView.setText(shareBottomSheetViewModel.getShare().getExpires());
|
||||
}
|
||||
}
|
||||
|
||||
private void setShareCalendar() {
|
||||
expiration = shareBottomSheetViewModel.getShare().getExpires().getTime();
|
||||
|
||||
bind.shareExpirationTextView.setText(UIUtil.getReadableDate(new Date(expiration)));
|
||||
|
||||
bind.shareExpirationTextView.setFocusable(false);
|
||||
bind.shareExpirationTextView.setOnLongClickListener(null);
|
||||
|
||||
bind.shareExpirationTextView.setOnClickListener(view -> {
|
||||
CalendarConstraints constraints = new CalendarConstraints.Builder()
|
||||
.setValidator(DateValidatorPointForward.now())
|
||||
.build();
|
||||
|
||||
datePicker = MaterialDatePicker.Builder.datePicker()
|
||||
.setCalendarConstraints(constraints)
|
||||
.setSelection(expiration)
|
||||
.build();
|
||||
|
||||
datePicker.addOnPositiveButtonClickListener(selection -> {
|
||||
expiration = selection;
|
||||
bind.shareExpirationTextView.setText(UIUtil.getReadableDate(new Date(selection)));
|
||||
});
|
||||
|
||||
datePicker.show(requireActivity().getSupportFragmentManager(), null);
|
||||
});
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
updateShare();
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean validateInput() {
|
||||
descriptionTextView = Objects.requireNonNull(bind.shareDescriptionTextView.getText()).toString().trim();
|
||||
expirationTextView = Objects.requireNonNull(bind.shareExpirationTextView.getText()).toString().trim();
|
||||
|
||||
if (TextUtils.isEmpty(descriptionTextView)) {
|
||||
bind.shareDescriptionTextView.setError(getString(R.string.error_required));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(expirationTextView)) {
|
||||
bind.shareExpirationTextView.setError(getString(R.string.error_required));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateShare() {
|
||||
shareBottomSheetViewModel.updateShare(descriptionTextView, expiration);
|
||||
homeViewModel.refreshShares(requireActivity());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -33,6 +36,7 @@ import com.cappielloantonio.tempo.service.DownloaderManager;
|
||||
import com.cappielloantonio.tempo.service.MediaManager;
|
||||
import com.cappielloantonio.tempo.service.MediaService;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.AlbumAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.AlbumHorizontalAdapter;
|
||||
@@ -40,6 +44,7 @@ import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.DiscoverSongAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.GridTrackAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.ShareHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.SimilarTrackAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.YearAdapter;
|
||||
@@ -76,6 +81,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
private AlbumHorizontalAdapter newReleasesAlbumAdapter;
|
||||
private YearAdapter yearAdapter;
|
||||
private GridTrackAdapter gridTrackAdapter;
|
||||
private ShareHorizontalAdapter shareHorizontalAdapter;
|
||||
|
||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||
|
||||
@@ -111,6 +117,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
initYearSongView();
|
||||
initRecentAddedAlbumView();
|
||||
initGridView();
|
||||
initSharesView();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,6 +127,12 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
initializeMediaBrowser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
refreshSharesView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
releaseMediaBrowser();
|
||||
@@ -227,6 +240,11 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
homeViewModel.refreshMostRecentlyAddedAlbums(getViewLifecycleOwner());
|
||||
return true;
|
||||
});
|
||||
|
||||
bind.sharesTextViewRefreshable.setOnLongClickListener(v -> {
|
||||
homeViewModel.refreshShares(getViewLifecycleOwner());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void initSyncStarredView() {
|
||||
@@ -649,6 +667,50 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
recentAddedAlbumSnapHelper.attachToRecyclerView(bind.recentlyAddedAlbumsRecyclerView);
|
||||
}
|
||||
|
||||
private void initSharesView() {
|
||||
bind.sharesRecyclerView.setHasFixedSize(true);
|
||||
|
||||
shareHorizontalAdapter = new ShareHorizontalAdapter(this);
|
||||
bind.sharesRecyclerView.setAdapter(shareHorizontalAdapter);
|
||||
if (Preferences.isSharingEnabled()) {
|
||||
homeViewModel.getShares(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), shares -> {
|
||||
if (shares == null) {
|
||||
if (bind != null)
|
||||
bind.sharesPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||
if (bind != null) bind.sharesSector.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (bind != null) bind.sharesPlaceholder.placeholder.setVisibility(View.GONE);
|
||||
if (bind != null)
|
||||
bind.sharesSector.setVisibility(!shares.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
if (bind != null)
|
||||
bind.sharesRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(shares.size(), 10), GridLayoutManager.HORIZONTAL, false));
|
||||
|
||||
shareHorizontalAdapter.setItems(shares);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SnapHelper starredTrackSnapHelper = new PagerSnapHelper();
|
||||
starredTrackSnapHelper.attachToRecyclerView(bind.sharesRecyclerView);
|
||||
|
||||
bind.sharesRecyclerView.addItemDecoration(
|
||||
new DotsIndicatorDecoration(
|
||||
getResources().getDimensionPixelSize(R.dimen.radius),
|
||||
getResources().getDimensionPixelSize(R.dimen.radius) * 4,
|
||||
getResources().getDimensionPixelSize(R.dimen.dots_height),
|
||||
requireContext().getResources().getColor(R.color.titleTextColor, null),
|
||||
requireContext().getResources().getColor(R.color.titleTextColor, null))
|
||||
);
|
||||
}
|
||||
|
||||
private void refreshSharesView() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> {
|
||||
if (Preferences.isSharingEnabled()) homeViewModel.refreshShares(getViewLifecycleOwner());
|
||||
};
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
private void setSlideViewOffset(ViewPager2 viewPager, float pageOffset, float pageMargin) {
|
||||
viewPager.setPageTransformer((page, position) -> {
|
||||
float myOffset = position * -(2 * pageOffset + pageMargin);
|
||||
@@ -748,4 +810,16 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
public void onYearClick(Bundle bundle) {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.songListPageFragment, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShareClick(Bundle bundle) {
|
||||
Share share = bundle.getParcelable(Constants.SHARE_OBJECT);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(share.getUrl())).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShareLongClick(Bundle bundle) {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.shareBottomSheetDialog, bundle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -32,7 +35,9 @@ import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.AlbumBottomSheetViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
@@ -43,6 +48,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
@UnstableApi
|
||||
public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
|
||||
private HomeViewModel homeViewModel;
|
||||
private AlbumBottomSheetViewModel albumBottomSheetViewModel;
|
||||
private AlbumID3 album;
|
||||
|
||||
@@ -55,6 +61,7 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
|
||||
album = this.requireArguments().getParcelable(Constants.ALBUM_OBJECT);
|
||||
|
||||
homeViewModel = new ViewModelProvider(requireActivity()).get(HomeViewModel.class);
|
||||
albumBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(AlbumBottomSheetViewModel.class);
|
||||
albumBottomSheetViewModel.setAlbum(album);
|
||||
|
||||
@@ -182,6 +189,19 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
|
||||
dismissBottomSheet();
|
||||
}));
|
||||
|
||||
TextView share = view.findViewById(R.id.share_text_view);
|
||||
share.setOnClickListener(v -> albumBottomSheetViewModel.shareAlbum().observe(getViewLifecycleOwner(), sharedAlbum -> {
|
||||
if (sharedAlbum != null) {
|
||||
ClipboardManager clipboardManager = (ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clipData = ClipData.newPlainText(getString(R.string.app_name), sharedAlbum.getUrl());
|
||||
clipboardManager.setPrimaryClip(clipData);
|
||||
refreshShares();
|
||||
dismissBottomSheet();
|
||||
}
|
||||
}));
|
||||
|
||||
share.setVisibility(Preferences.isSharingEnabled() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -210,4 +230,8 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
private void releaseMediaBrowser() {
|
||||
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
||||
}
|
||||
|
||||
private void refreshShares() {
|
||||
homeViewModel.refreshShares(requireActivity());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
import com.cappielloantonio.tempo.ui.dialog.ShareUpdateDialog;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.ShareBottomSheetViewModel;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
@UnstableApi
|
||||
public class ShareBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
|
||||
|
||||
private HomeViewModel homeViewModel;
|
||||
private ShareBottomSheetViewModel shareBottomSheetViewModel;
|
||||
private Share share;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.bottom_sheet_share_dialog, container, false);
|
||||
|
||||
share = this.requireArguments().getParcelable(Constants.SHARE_OBJECT);
|
||||
|
||||
homeViewModel = new ViewModelProvider(requireActivity()).get(HomeViewModel.class);
|
||||
shareBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(ShareBottomSheetViewModel.class);
|
||||
shareBottomSheetViewModel.setShare(share);
|
||||
|
||||
init(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void init(View view) {
|
||||
ImageView shareCover = view.findViewById(R.id.share_cover_image_view);
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(requireContext(), shareBottomSheetViewModel.getShare().getEntries().get(0).getCoverArtId(), CustomGlideRequest.ResourceType.Unknown)
|
||||
.build()
|
||||
.into(shareCover);
|
||||
|
||||
TextView shareTitle = view.findViewById(R.id.share_title_text_view);
|
||||
shareTitle.setText(shareBottomSheetViewModel.getShare().getDescription());
|
||||
shareTitle.setSelected(true);
|
||||
|
||||
TextView shareSubtitle = view.findViewById(R.id.share_subtitle_text_view);
|
||||
shareSubtitle.setText(requireContext().getString(R.string.share_subtitle_item, UIUtil.getReadableDate(share.getExpires())));
|
||||
shareSubtitle.setSelected(true);
|
||||
|
||||
TextView copyLink = view.findViewById(R.id.copy_link_text_view);
|
||||
copyLink.setOnClickListener(v -> {
|
||||
ClipboardManager clipboardManager = (ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clipData = ClipData.newPlainText(getString(R.string.app_name), shareBottomSheetViewModel.getShare().getUrl());
|
||||
clipboardManager.setPrimaryClip(clipData);
|
||||
dismissBottomSheet();
|
||||
});
|
||||
|
||||
TextView updateShare = view.findViewById(R.id.update_share_preferences_text_view);
|
||||
updateShare.setOnClickListener(v -> {
|
||||
// refreshShares();
|
||||
showUpdateShareDialog();
|
||||
dismissBottomSheet();
|
||||
});
|
||||
|
||||
TextView deleteShare = view.findViewById(R.id.delete_share_text_view);
|
||||
deleteShare.setOnClickListener(v -> {
|
||||
deleteShare();
|
||||
refreshShares();
|
||||
dismissBottomSheet();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismissBottomSheet();
|
||||
}
|
||||
|
||||
private void dismissBottomSheet() {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
private void showUpdateShareDialog() {
|
||||
ShareUpdateDialog dialog = new ShareUpdateDialog();
|
||||
dialog.show(requireActivity().getSupportFragmentManager(), null);
|
||||
}
|
||||
|
||||
private void refreshShares() {
|
||||
homeViewModel.refreshShares(getParentFragment());
|
||||
}
|
||||
|
||||
private void deleteShare() {
|
||||
shareBottomSheetViewModel.deleteShare();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -30,14 +33,15 @@ import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.SongBottomSheetViewModel;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
@UnstableApi
|
||||
public class SongBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
|
||||
private static final String TAG = "SongBottomSheetDialog";
|
||||
|
||||
private HomeViewModel homeViewModel;
|
||||
private SongBottomSheetViewModel songBottomSheetViewModel;
|
||||
private Child song;
|
||||
|
||||
@@ -50,6 +54,7 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
|
||||
song = requireArguments().getParcelable(Constants.TRACK_OBJECT);
|
||||
|
||||
homeViewModel = new ViewModelProvider(requireActivity()).get(HomeViewModel.class);
|
||||
songBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(SongBottomSheetViewModel.class);
|
||||
songBottomSheetViewModel.setSong(song);
|
||||
|
||||
@@ -202,6 +207,19 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
|
||||
dismissBottomSheet();
|
||||
}));
|
||||
|
||||
TextView share = view.findViewById(R.id.share_text_view);
|
||||
share.setOnClickListener(v -> songBottomSheetViewModel.shareTrack().observe(getViewLifecycleOwner(), sharedTrack -> {
|
||||
if (sharedTrack != null) {
|
||||
ClipboardManager clipboardManager = (ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clipData = ClipData.newPlainText(getString(R.string.app_name), sharedTrack.getUrl());
|
||||
clipboardManager.setPrimaryClip(clipData);
|
||||
refreshShares();
|
||||
dismissBottomSheet();
|
||||
}
|
||||
}));
|
||||
|
||||
share.setVisibility(Preferences.isSharingEnabled() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -229,4 +247,8 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
private void releaseMediaBrowser() {
|
||||
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
||||
}
|
||||
|
||||
private void refreshShares() {
|
||||
homeViewModel.refreshShares(requireActivity());
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,8 @@ object Constants {
|
||||
const val DOWNLOAD_GROUP_TITLE = "download_group_title"
|
||||
const val DOWNLOAD_GROUP_SUBTITLE = "download_group_subtitle"
|
||||
|
||||
const val SHARE_OBJECT = "share_object"
|
||||
|
||||
const val PLAYABLE_MEDIA_LIMIT = 100
|
||||
const val PRE_PLAYABLE_MEDIA = 15
|
||||
}
|
||||
@@ -50,6 +50,8 @@ public class MusicUtil {
|
||||
uri.append("&maxBitRate=").append(getBitratePreference());
|
||||
if (!Preferences.isServerPrioritized())
|
||||
uri.append("&format=").append(getTranscodingFormatPreference());
|
||||
if (Preferences.askForEstimateContentLength())
|
||||
uri.append("&estimateContentLength=true");
|
||||
|
||||
uri.append("&id=").append(id);
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ object Preferences {
|
||||
private const val AUDIO_TRANSCODE_DOWNLOAD_PRIORITY = "audio_transcode_download_priority"
|
||||
private const val MAX_BITRATE_DOWNLOAD = "max_bitrate_download"
|
||||
private const val AUDIO_TRANSCODE_FORMAT_DOWNLOAD = "audio_transcode_format_download"
|
||||
private const val SHARE = "share"
|
||||
private const val ESTIMATE_CONTENT_LENGTH = "estimate_content_length"
|
||||
|
||||
@JvmStatic
|
||||
fun getServer(): String? {
|
||||
@@ -313,4 +315,14 @@ object Preferences {
|
||||
fun getAudioTranscodeFormatTranscodedDownload(): String {
|
||||
return App.getInstance().preferences.getString(AUDIO_TRANSCODE_FORMAT_DOWNLOAD, "raw")!!
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isSharingEnabled(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(SHARE, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun askForEstimateContentLength(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(ESTIMATE_CONTENT_LENGTH, false)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,9 @@ import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -84,4 +86,9 @@ public class UIUtil {
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String getReadableDate(Date date) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("dd MMM, yyyy", Locale.getDefault());
|
||||
return formatter.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
||||
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||
import com.cappielloantonio.tempo.repository.SharingRepository;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
import com.cappielloantonio.tempo.util.NetworkUtil;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -23,6 +25,7 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
||||
private final AlbumRepository albumRepository;
|
||||
private final ArtistRepository artistRepository;
|
||||
private final FavoriteRepository favoriteRepository;
|
||||
private final SharingRepository sharingRepository;
|
||||
|
||||
private AlbumID3 album;
|
||||
|
||||
@@ -32,6 +35,7 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
||||
albumRepository = new AlbumRepository();
|
||||
artistRepository = new ArtistRepository();
|
||||
favoriteRepository = new FavoriteRepository();
|
||||
sharingRepository = new SharingRepository();
|
||||
}
|
||||
|
||||
public AlbumID3 getAlbum() {
|
||||
@@ -66,6 +70,10 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
public MutableLiveData<Share> shareAlbum() {
|
||||
return sharingRepository.createShare(album.getId(), album.getName(), null);
|
||||
}
|
||||
|
||||
private void removeFavoriteOffline() {
|
||||
favoriteRepository.starLater(null, album.getId(), null, false);
|
||||
album.setStarred(null);
|
||||
|
||||
@@ -15,10 +15,12 @@ import com.cappielloantonio.tempo.repository.AlbumRepository;
|
||||
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||
import com.cappielloantonio.tempo.repository.ChronologyRepository;
|
||||
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||
import com.cappielloantonio.tempo.repository.SharingRepository;
|
||||
import com.cappielloantonio.tempo.repository.SongRepository;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -36,6 +38,7 @@ public class HomeViewModel extends AndroidViewModel {
|
||||
private final ArtistRepository artistRepository;
|
||||
private final ChronologyRepository chronologyRepository;
|
||||
private final FavoriteRepository favoriteRepository;
|
||||
private final SharingRepository sharingRepository;
|
||||
|
||||
private final MutableLiveData<List<Child>> dicoverSongSample = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<List<AlbumID3>> newReleasedAlbum = new MutableLiveData<>(null);
|
||||
@@ -54,6 +57,8 @@ public class HomeViewModel extends AndroidViewModel {
|
||||
private final MutableLiveData<List<Child>> mediaInstantMix = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<List<Child>> artistInstantMix = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<List<Child>> artistBestOf = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<List<Share>> shares = new MutableLiveData<>(null);
|
||||
|
||||
|
||||
public HomeViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
@@ -63,6 +68,7 @@ public class HomeViewModel extends AndroidViewModel {
|
||||
artistRepository = new ArtistRepository();
|
||||
chronologyRepository = new ChronologyRepository();
|
||||
favoriteRepository = new FavoriteRepository();
|
||||
sharingRepository = new SharingRepository();
|
||||
|
||||
setOfflineFavorite();
|
||||
}
|
||||
@@ -204,6 +210,14 @@ public class HomeViewModel extends AndroidViewModel {
|
||||
return artistBestOf;
|
||||
}
|
||||
|
||||
public LiveData<List<Share>> getShares(LifecycleOwner owner) {
|
||||
if (shares.getValue() == null) {
|
||||
sharingRepository.getShares().observe(owner, shares::postValue);
|
||||
}
|
||||
|
||||
return shares;
|
||||
}
|
||||
|
||||
public LiveData<List<Child>> getAllStarredTracks() {
|
||||
return songRepository.getStarredSongs(false, -1);
|
||||
}
|
||||
@@ -248,6 +262,10 @@ public class HomeViewModel extends AndroidViewModel {
|
||||
albumRepository.getAlbums("recent", 20, null, null).observe(owner, recentlyPlayedAlbumSample::postValue);
|
||||
}
|
||||
|
||||
public void refreshShares(LifecycleOwner owner) {
|
||||
sharingRepository.getShares().observe(owner, this.shares::postValue);
|
||||
}
|
||||
|
||||
public void setOfflineFavorite() {
|
||||
ArrayList<Favorite> favorites = getFavorites();
|
||||
ArrayList<Favorite> favoritesToSave = getFavoritesToSave(favorites);
|
||||
|
||||
@@ -8,8 +8,10 @@ import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.cappielloantonio.tempo.repository.PlaylistRepository;
|
||||
import com.cappielloantonio.tempo.repository.SharingRepository;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -20,6 +22,7 @@ public class PlaylistEditorViewModel extends AndroidViewModel {
|
||||
private static final String TAG = "PlaylistEditorViewModel";
|
||||
|
||||
private final PlaylistRepository playlistRepository;
|
||||
private final SharingRepository sharingRepository;
|
||||
|
||||
private Child toAdd;
|
||||
private Playlist toEdit;
|
||||
@@ -30,6 +33,7 @@ public class PlaylistEditorViewModel extends AndroidViewModel {
|
||||
super(application);
|
||||
|
||||
playlistRepository = new PlaylistRepository();
|
||||
sharingRepository = new SharingRepository();
|
||||
}
|
||||
|
||||
public void createPlaylist(String name) {
|
||||
@@ -92,4 +96,8 @@ public class PlaylistEditorViewModel extends AndroidViewModel {
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
public MutableLiveData<Share> sharePlaylist() {
|
||||
return sharingRepository.createShare(toEdit.getId(), toEdit.getName(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.cappielloantonio.tempo.viewmodel;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
|
||||
import com.cappielloantonio.tempo.repository.SharingRepository;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
|
||||
public class ShareBottomSheetViewModel extends AndroidViewModel {
|
||||
private final SharingRepository sharingRepository;
|
||||
|
||||
private Share share;
|
||||
|
||||
public ShareBottomSheetViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
|
||||
sharingRepository = new SharingRepository();
|
||||
}
|
||||
|
||||
public Share getShare() {
|
||||
return share;
|
||||
}
|
||||
|
||||
public void setShare(Share share) {
|
||||
this.share = share;
|
||||
}
|
||||
|
||||
public void updateShare(String description, long expires) {
|
||||
sharingRepository.updateShare(share.getId(), description, expires);
|
||||
}
|
||||
|
||||
public void deleteShare() {
|
||||
sharingRepository.deleteShare(share.getId());
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,12 @@ import com.cappielloantonio.tempo.model.Download;
|
||||
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
||||
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||
import com.cappielloantonio.tempo.repository.SharingRepository;
|
||||
import com.cappielloantonio.tempo.repository.SongRepository;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.NetworkUtil;
|
||||
@@ -34,6 +36,7 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
|
||||
private final AlbumRepository albumRepository;
|
||||
private final ArtistRepository artistRepository;
|
||||
private final FavoriteRepository favoriteRepository;
|
||||
private final SharingRepository sharingRepository;
|
||||
|
||||
private Child song;
|
||||
|
||||
@@ -46,6 +49,7 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
|
||||
albumRepository = new AlbumRepository();
|
||||
artistRepository = new ArtistRepository();
|
||||
favoriteRepository = new FavoriteRepository();
|
||||
sharingRepository = new SharingRepository();
|
||||
}
|
||||
|
||||
public Child getSong() {
|
||||
@@ -128,4 +132,8 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
|
||||
|
||||
return instantMix;
|
||||
}
|
||||
|
||||
public MutableLiveData<Share> shareTrack() {
|
||||
return sharingRepository.createShare(song.getId(), song.getTitle(), null);
|
||||
}
|
||||
}
|
||||
|
||||
9
app/src/main/res/drawable/ic_share.xml
Normal file
9
app/src/main/res/drawable/ic_share.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@color/titleTextColor"
|
||||
android:pathData="M696,864Q646,864 611,829Q576,794 576,744Q576,736 577,729.5Q578,723 580,715L342,570Q327,586 306.65,593Q286.29,600 264,600Q214,600 179,565Q144,530 144,480Q144,430 179,395Q214,360 264,360Q286,360 306.5,367.5Q327,375 342,390L580,245Q578,237 577,230.5Q576,224 576,216Q576,166 611,131Q646,96 696,96Q746,96 781,131Q816,166 816,216Q816,266 781,301Q746,336 696,336Q673.71,336 653.35,329Q633,322 618,306L380,451Q382,459 383,465.5Q384,472 384,480Q384,488 383,494.5Q382,501 380,509L618,654Q633,637 653.35,630.5Q673.71,624 696,624Q746,624 781,659Q816,694 816,744Q816,794 781,829Q746,864 696,864Z"/>
|
||||
</vector>
|
||||
@@ -172,5 +172,19 @@
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/album_bottom_sheet_go_to_artist" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/album_bottom_sheet_share"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
106
app/src/main/res/layout/bottom_sheet_share_dialog.xml
Normal file
106
app/src/main/res/layout/bottom_sheet_share_dialog.xml
Normal file
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:clipChildren="false">
|
||||
|
||||
<!-- Header -->
|
||||
<ImageView
|
||||
android:id="@+id/share_cover_image_view"
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="54dp"
|
||||
android:layout_margin="2dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_title_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toTopOf="@+id/share_subtitle_text_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_subtitle_text_view"
|
||||
style="@style/LabelSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/share_title_text_view" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/option_linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="12dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/copy_link_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/share_bottom_sheet_copy_link" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/update_share_preferences_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/share_bottom_sheet_update" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/delete_share_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/share_bottom_sheet_delete" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -195,5 +195,18 @@
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/song_bottom_sheet_go_to_artist" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/song_bottom_sheet_share"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -4,26 +4,47 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:textColorHint="?android:textColorHint"
|
||||
app:endIconMode="clear_text"
|
||||
app:endIconTint="?android:textColorSecondary"
|
||||
app:errorEnabled="true">
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="12dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/playlist_name_text_view"
|
||||
android:layout_width="match_parent"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/playlist_editor_dialog_hint_name"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:textCursorDrawable="@null" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
android:layout_weight="1"
|
||||
android:textColorHint="?android:textColorHint"
|
||||
app:endIconMode="clear_text"
|
||||
app:endIconTint="?android:textColorSecondary"
|
||||
app:errorEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/playlist_name_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/playlist_editor_dialog_hint_name"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:textCursorDrawable="@null" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/playlist_share_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:background="@drawable/ic_share"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/playlist_song_recycler_view"
|
||||
|
||||
51
app/src/main/res/layout/dialog_share_update.xml
Normal file
51
app/src/main/res/layout/dialog_share_update.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:textColorHint="?android:textColorHint"
|
||||
app:endIconMode="clear_text"
|
||||
app:endIconTint="?android:textColorSecondary"
|
||||
app:errorEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/share_description_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/share_update_dialog_hint_description"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:textCursorDrawable="@null" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:textColorHint="?android:textColorHint"
|
||||
app:endIconMode="clear_text"
|
||||
app:endIconTint="?android:textColorSecondary"
|
||||
app:errorEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/share_expiration_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:longClickable="false"
|
||||
android:textIsSelectable="false"
|
||||
android:hint="@string/share_update_dialog_hint_expiration_date"
|
||||
android:inputType="textShortMessage"
|
||||
android:textCursorDrawable="@null" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
@@ -753,6 +753,40 @@
|
||||
android:id="@+id/home_recently_added_albums_placeholder"
|
||||
layout="@layout/item_placeholder_album"
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- Shares -->
|
||||
<LinearLayout
|
||||
android:id="@+id/shares_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/shares_text_view_refreshable"
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/home_title_shares" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/shares_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/shares_placeholder"
|
||||
layout="@layout/item_placeholder_horizontal"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
||||
65
app/src/main/res/layout/item_horizontal_share.xml
Normal file
65
app/src/main/res/layout/item_horizontal_share.xml
Normal file
@@ -0,0 +1,65 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clipChildren="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingBottom="2dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/share_cover_image_view"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_title_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toTopOf="@+id/share_subtitle_text_view"
|
||||
app:layout_constraintEnd_toStartOf="@+id/share_button"
|
||||
app:layout_constraintStart_toEndOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_subtitle_text_view"
|
||||
style="@style/LabelSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintEnd_toStartOf="@+id/share_button"
|
||||
app:layout_constraintStart_toEndOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/share_title_text_view" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/share_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/share_cover_image_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/share_cover_image_view">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:background="@drawable/ic_more_vert"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless" />
|
||||
</FrameLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -330,4 +330,9 @@
|
||||
android:name="com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog.PodcastChannelBottomSheetDialog"
|
||||
android:label="PodcastChannelBottomSheetDialog"
|
||||
tools:layout="@layout/bottom_sheet_podcast_channel_dialog" />
|
||||
<dialog
|
||||
android:id="@+id/shareBottomSheetDialog"
|
||||
android:name="com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog.ShareBottomSheetDialog"
|
||||
android:label="ShareBottomSheetDialog"
|
||||
tools:layout="@layout/bottom_sheet_share_dialog" />
|
||||
</navigation>
|
||||
@@ -89,6 +89,7 @@
|
||||
<string name="home_title_recently_added">Kürzlich hinzugefügt</string>
|
||||
<string name="home_title_recently_added_see_all_button">Alle zeigen</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Alle mischen</string>
|
||||
<string name="home_title_shares">Shares</string>
|
||||
<string name="home_title_starred_albums">★ Lieblingsalben</string>
|
||||
<string name="home_title_starred_albums_see_all_button">Alle zeigen</string>
|
||||
<string name="home_title_starred_artists">★ Lieblingskünstler</string>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<string name="album_bottom_sheet_play_next">Play next</string>
|
||||
<string name="album_bottom_sheet_remove_all">Remove all</string>
|
||||
<string name="album_bottom_sheet_shuffle">Shuffle</string>
|
||||
<string name="album_bottom_sheet_share">Share</string>
|
||||
<string name="album_catalogue_title">Albums</string>
|
||||
<string name="album_catalogue_title_expanded">Browse Albums</string>
|
||||
<string name="album_error_retrieving_artist">Error retrieving artist</string>
|
||||
@@ -89,6 +90,7 @@
|
||||
<string name="home_title_recently_added">Recently added</string>
|
||||
<string name="home_title_recently_added_see_all_button">See all</string>
|
||||
<string name="home_title_discovery_shuffle_all_button">Shuffle all</string>
|
||||
<string name="home_title_shares">Shares</string>
|
||||
<string name="home_title_starred_albums">★ Starred albums</string>
|
||||
<string name="home_title_starred_albums_see_all_button">See all</string>
|
||||
<string name="home_title_starred_artists">★ Starred artists</string>
|
||||
@@ -188,6 +190,8 @@
|
||||
<string name="settings_audio_transcode_download_summary">If enabled, Tempo will download transcoded tracks.</string>
|
||||
<string name="settings_audio_transcode_download_title">Download transcoded tracks</string>
|
||||
<string name="settings_audio_transcode_download_format">Transcode format</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">If enabled, the server will be asked for the estimated duration of the track.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">Estimate content length</string>
|
||||
<string name="settings_audio_transcode_priority_summary">If enabled, Tempo will not force stream the track with the transcode settings below.</string>
|
||||
<string name="settings_audio_transcode_priority_title">Prioritize server transcode settings</string>
|
||||
<string name="settings_audio_transcode_priority_toast">Priority on transcoding of track given to server</string>
|
||||
@@ -227,9 +231,12 @@
|
||||
<string name="settings_rounded_corner_size">Corners size</string>
|
||||
<string name="settings_rounded_corner_size_summary">Sets the magnitude of the curvature angle.</string>
|
||||
<string name="settings_scan_title">Scan library</string>
|
||||
<string name="settings_share_title">Enable music sharing</string>
|
||||
<string name="settings_summary_replay_gain">Replay gain is a feature that allows you to adjust the volume level of audio tracks for a consistent listening experience. This setting is only effective if the track contains the necessary metadata.</string>
|
||||
<string name="settings_summary_share">Allows the user to share music via a link. The functionality must be supported and enabled server-side and is limited to individual tracks, albums and playlists.</string>
|
||||
<string name="settings_summary_syncing">Returns the state of the play queue for this user. This includes the tracks in the play queue, the currently playing track, and the position within this track. The server must support this feature.</string>
|
||||
<string name="settings_summary_transcoding">Priority given to the transcoding mode. If set to \"Direct play\" the bitrate of the file will not be changed.</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">When the file is transcoded on the fly, the client usually does not show the track length. It is possible to request the servers that support the functionality to estimate the duration of the track being played, but the response times may take longer.</string>
|
||||
<string name="settings_summary_transcoding_download">Download transcoded media. If enabled, the download endpoint will not be used, but the following settings. \n\n If \"Transcode format\" is set to \"Direct play\" the bitrate of the file will not be changed.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">If enabled, starred tracks will be downloaded for offline use.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">Sync starred tracks for offline use</string>
|
||||
@@ -237,6 +244,7 @@
|
||||
<string name="settings_title_data">Data</string>
|
||||
<string name="settings_title_general">General</string>
|
||||
<string name="settings_title_replay_gain">Replay Gain</string>
|
||||
<string name="settings_title_share">Share</string>
|
||||
<string name="settings_title_syncing">Syncing</string>
|
||||
<string name="settings_title_transcoding">Transcoding</string>
|
||||
<string name="settings_title_transcoding_download">Transcoding Download</string>
|
||||
@@ -246,6 +254,15 @@
|
||||
<string name="settings_version_title">Version</string>
|
||||
<string name="settings_wifi_only_title">Stream via Wi-Fi only alert</string>
|
||||
<string name="settings_wifi_only_summary">Ask for user confirmation before streaming over mobile network.</string>
|
||||
<string name="share_bottom_sheet_copy_link">Copy link</string>
|
||||
<string name="share_bottom_sheet_delete">Delete share</string>
|
||||
<string name="share_bottom_sheet_update">Update share</string>
|
||||
<string name="share_subtitle_item">Expiration date: %1$s</string>
|
||||
<string name="share_update_dialog_negative_button">Cancel</string>
|
||||
<string name="share_update_dialog_positive_button">Save</string>
|
||||
<string name="share_update_dialog_hint_description">Description</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Expiration date</string>
|
||||
<string name="share_update_dialog_title">Share</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">Add to playlist</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">Add to queue</string>
|
||||
<string name="song_bottom_sheet_download">Download</string>
|
||||
@@ -253,6 +270,7 @@
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">Error retrieving artist</string>
|
||||
<string name="song_bottom_sheet_go_to_album">Go to album</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">Go to artist</string>
|
||||
<string name="song_bottom_sheet_share">Share</string>
|
||||
<string name="song_bottom_sheet_instant_mix">Instant mix</string>
|
||||
<string name="song_bottom_sheet_play_next">Play next</string>
|
||||
<string name="song_bottom_sheet_rate">Rate</string>
|
||||
|
||||
@@ -158,6 +158,16 @@
|
||||
app:key="max_bitrate_mobile"
|
||||
app:title="@string/settings_max_bitrate_mobile"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_audio_transcode_estimate_content_length_title"
|
||||
android:defaultValue="false"
|
||||
android:summary="@string/settings_audio_transcode_estimate_content_length_summary"
|
||||
android:key="estimate_content_length" />
|
||||
|
||||
<Preference
|
||||
app:selectable="false"
|
||||
app:summary="@string/settings_summary_transcoding_estimate_content_length" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_title_transcoding_download">
|
||||
@@ -212,6 +222,18 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_title_share">
|
||||
<Preference
|
||||
app:selectable="false"
|
||||
app:summary="@string/settings_summary_share" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_share_title"
|
||||
android:defaultValue="false"
|
||||
android:key="share" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_title_syncing">
|
||||
<Preference
|
||||
app:selectable="false"
|
||||
|
||||
Reference in New Issue
Block a user