mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-01-31 14:43:36 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc13beca49 | ||
|
|
6399488819 | ||
|
|
69c1b93d28 | ||
|
|
50c240793a | ||
|
|
80f7783b7d | ||
|
|
259c8b3599 | ||
|
|
ba1295440b | ||
|
|
9e6178037c | ||
|
|
47302815c1 | ||
|
|
c81bfe1457 | ||
|
|
f413b5d498 | ||
|
|
feae1b8bdd | ||
|
|
57bb51fb8d | ||
|
|
0f1364502a | ||
|
|
a508ec0af6 | ||
|
|
a9b264004c | ||
|
|
803b61430f | ||
|
|
25367512c0 | ||
|
|
e3720f4a50 | ||
|
|
1168cc58f4 | ||
|
|
c711b387bb | ||
|
|
a3fe0de233 | ||
|
|
922153837d | ||
|
|
960dd6c76a | ||
|
|
fcb7135c3d |
6
.github/workflows/github_release.yml
vendored
6
.github/workflows/github_release.yml
vendored
@@ -34,13 +34,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Build APK
|
- name: Build APK
|
||||||
id: build
|
id: build
|
||||||
run: bash ./gradlew assembleRelease
|
run: bash ./gradlew assembleTempoRelease
|
||||||
|
|
||||||
- name: Sign APK
|
- name: Sign APK
|
||||||
id: sign_apk
|
id: sign_apk
|
||||||
uses: r0adkll/sign-android-release@v1
|
uses: r0adkll/sign-android-release@v1
|
||||||
with:
|
with:
|
||||||
releaseDirectory: app/build/outputs/apk/release
|
releaseDirectory: app/build/outputs/apk/tempo/release
|
||||||
signingKeyBase64: ${{ secrets.KEYSTORE_BASE64 }}
|
signingKeyBase64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||||
alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||||
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
asset_path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
||||||
asset_name: app-release.apk
|
asset_name: app-tempo-release.apk
|
||||||
asset_content_type: application/zip
|
asset_content_type: application/zip
|
||||||
|
|
||||||
# - name: Upload AAB
|
# - name: Upload AAB
|
||||||
|
|||||||
@@ -7,11 +7,8 @@ android {
|
|||||||
buildToolsVersion '33.0.0'
|
buildToolsVersion '33.0.0'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'com.cappielloantonio.tempo'
|
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 11
|
|
||||||
versionName '3.4.4'
|
|
||||||
|
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
|
|
||||||
@@ -25,6 +22,24 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flavorDimensions "default"
|
||||||
|
|
||||||
|
productFlavors {
|
||||||
|
tempo {
|
||||||
|
dimension "default"
|
||||||
|
applicationId 'com.cappielloantonio.tempo'
|
||||||
|
versionCode 12
|
||||||
|
versionName '3.4.5'
|
||||||
|
}
|
||||||
|
|
||||||
|
notquitemy {
|
||||||
|
dimension "default"
|
||||||
|
applicationId "com.cappielloantonio.notquitemy.tempo"
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
@@ -54,8 +69,6 @@ dependencies {
|
|||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
// AndroidX
|
// AndroidX
|
||||||
implementation 'androidx.core:core-ktx:1.10.1'
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||||
@@ -63,13 +76,8 @@ dependencies {
|
|||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
||||||
implementation 'androidx.room:room-runtime:2.5.2'
|
implementation 'androidx.room:room-runtime:2.5.2'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
|
|
||||||
// Google GMS
|
|
||||||
implementation 'com.google.android.gms:play-services-cast-framework:21.3.0'
|
|
||||||
|
|
||||||
// Android Material
|
// Android Material
|
||||||
implementation 'com.google.android.material:material:1.9.0'
|
implementation 'com.google.android.material:material:1.9.0'
|
||||||
|
|
||||||
@@ -78,23 +86,17 @@ dependencies {
|
|||||||
implementation 'com.github.bumptech.glide:annotations:4.15.1'
|
implementation 'com.github.bumptech.glide:annotations:4.15.1'
|
||||||
|
|
||||||
// Media3
|
// Media3
|
||||||
implementation 'androidx.media3:media3-session:1.0.2'
|
implementation 'androidx.media3:media3-session:1.1.0'
|
||||||
implementation 'androidx.media3:media3-common:1.0.2'
|
implementation 'androidx.media3:media3-common:1.1.0'
|
||||||
implementation 'androidx.media3:media3-exoplayer:1.0.2'
|
implementation 'androidx.media3:media3-exoplayer:1.1.0'
|
||||||
implementation 'androidx.media3:media3-ui:1.0.2'
|
implementation 'androidx.media3:media3-ui:1.1.0'
|
||||||
implementation 'androidx.media3:media3-cast:1.0.2'
|
tempoImplementation 'androidx.media3:media3-cast:1.1.0'
|
||||||
|
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
|
||||||
annotationProcessor 'androidx.room:room-compiler:2.5.2'
|
annotationProcessor 'androidx.room:room-compiler:2.5.2'
|
||||||
|
|
||||||
// Retrofit
|
// Retrofit
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.6'
|
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.11'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
|
|
||||||
// Crash Report
|
|
||||||
// debugImplementation 'com.balsikandar.android:crashreporter:1.1.0'
|
|
||||||
|
|
||||||
// DB debug
|
|
||||||
// debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.cappielloantonio.tempo.model
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class ReplayGain(
|
||||||
|
var trackGain: Float = 0f,
|
||||||
|
var albumGain: Float = 0f,
|
||||||
|
)
|
||||||
@@ -11,6 +11,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
|||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.SearchResult2;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult3;
|
import com.cappielloantonio.tempo.subsonic.models.SearchResult3;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -24,7 +25,28 @@ import retrofit2.Response;
|
|||||||
public class SearchingRepository {
|
public class SearchingRepository {
|
||||||
private final RecentSearchDao recentSearchDao = AppDatabase.getInstance().recentSearchDao();
|
private final RecentSearchDao recentSearchDao = AppDatabase.getInstance().recentSearchDao();
|
||||||
|
|
||||||
public MutableLiveData<SearchResult3> search(String query) {
|
public MutableLiveData<SearchResult2> search2(String query) {
|
||||||
|
MutableLiveData<SearchResult2> result = new MutableLiveData<>();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getSearchingClient()
|
||||||
|
.search3(query, 20, 20, 20)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
result.setValue(response.body().getSubsonicResponse().getSearchResult2());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MutableLiveData<SearchResult3> search3(String query) {
|
||||||
MutableLiveData<SearchResult3> result = new MutableLiveData<>();
|
MutableLiveData<SearchResult3> result = new MutableLiveData<>();
|
||||||
|
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
|
|||||||
@@ -141,6 +141,10 @@ public class MainActivity extends BaseActivity {
|
|||||||
handler.postDelayed(runnable, 100);
|
handler.postDelayed(runnable, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void expandBottomSheet() {
|
||||||
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||||
|
}
|
||||||
|
|
||||||
public void setBottomSheetDraggableState(Boolean isDraggable) {
|
public void setBottomSheetDraggableState(Boolean isDraggable) {
|
||||||
bottomSheetBehavior.setDraggable(isDraggable);
|
bottomSheetBehavior.setDraggable(isDraggable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ import androidx.media3.session.SessionToken;
|
|||||||
import com.cappielloantonio.tempo.service.DownloaderService;
|
import com.cappielloantonio.tempo.service.DownloaderService;
|
||||||
import com.cappielloantonio.tempo.service.MediaService;
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.BatteryOptimizationDialog;
|
import com.cappielloantonio.tempo.ui.dialog.BatteryOptimizationDialog;
|
||||||
|
import com.cappielloantonio.tempo.util.Flavors;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
import com.cappielloantonio.tempo.util.UIUtil;
|
|
||||||
import com.google.android.gms.cast.framework.CastContext;
|
|
||||||
import com.google.android.material.elevation.SurfaceColors;
|
import com.google.android.material.elevation.SurfaceColors;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
@@ -34,7 +33,7 @@ public class BaseActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
initializeCastContext();
|
Flavors.initializeCastContext(this);
|
||||||
initializeDownloader();
|
initializeDownloader();
|
||||||
checkBatteryOptimization();
|
checkBatteryOptimization();
|
||||||
checkPermission();
|
checkPermission();
|
||||||
@@ -54,7 +53,7 @@ public class BaseActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkBatteryOptimization() {
|
private void checkBatteryOptimization() {
|
||||||
if (detectBatteryOptimization() && Preferences.askForOptimization()) {
|
if (detectBatteryOptimization() && Boolean.TRUE.equals(Preferences.askForOptimization())) {
|
||||||
showBatteryOptimizationDialog();
|
showBatteryOptimizationDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,10 +97,6 @@ public class BaseActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeCastContext() {
|
|
||||||
if (UIUtil.isCastApiAvailable(this)) CastContext.getSharedInstance(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setNavigationBarColor() {
|
private void setNavigationBarColor() {
|
||||||
getWindow().setNavigationBarColor(SurfaceColors.getColorForElevation(this, 10));
|
getWindow().setNavigationBarColor(SurfaceColors.getColorForElevation(this, 10));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
|||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
|
|
||||||
if (children.get(getBindingAdapterPosition()).isDir()) {
|
if (children.get(getBindingAdapterPosition()).isDir()) {
|
||||||
bundle.putParcelable(Constants.MUSIC_DIRECTORY_OBJECT, children.get(getBindingAdapterPosition()));
|
bundle.putString(Constants.MUSIC_DIRECTORY_ID, children.get(getBindingAdapterPosition()).getId());
|
||||||
click.onMusicDirectoryClick(bundle);
|
click.onMusicDirectoryClick(bundle);
|
||||||
} else {
|
} else {
|
||||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(children));
|
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(children));
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.Vi
|
|||||||
|
|
||||||
public void onClick() {
|
public void onClick() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelable(Constants.MUSIC_INDEX_OBJECT, artists.get(getBindingAdapterPosition()));
|
bundle.putString(Constants.MUSIC_DIRECTORY_ID, artists.get(getBindingAdapterPosition()).getId());
|
||||||
click.onMusicIndexClick(bundle);
|
click.onMusicIndexClick(bundle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.lifecycle.ViewModelProvider;
|
|||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.session.MediaBrowser;
|
import androidx.media3.session.MediaBrowser;
|
||||||
import androidx.media3.session.SessionToken;
|
import androidx.media3.session.SessionToken;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
@@ -20,17 +21,12 @@ import com.cappielloantonio.tempo.databinding.FragmentDirectoryBinding;
|
|||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
import com.cappielloantonio.tempo.service.MediaManager;
|
import com.cappielloantonio.tempo.service.MediaManager;
|
||||||
import com.cappielloantonio.tempo.service.MediaService;
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.MusicDirectoryAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.MusicDirectoryAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.viewmodel.DirectoryViewModel;
|
import com.cappielloantonio.tempo.viewmodel.DirectoryViewModel;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class DirectoryFragment extends Fragment implements ClickCallback {
|
public class DirectoryFragment extends Fragment implements ClickCallback {
|
||||||
private static final String TAG = "DirectoryFragment";
|
private static final String TAG = "DirectoryFragment";
|
||||||
@@ -52,9 +48,7 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
|||||||
directoryViewModel = new ViewModelProvider(requireActivity()).get(DirectoryViewModel.class);
|
directoryViewModel = new ViewModelProvider(requireActivity()).get(DirectoryViewModel.class);
|
||||||
|
|
||||||
initAppBar();
|
initAppBar();
|
||||||
initButtons();
|
|
||||||
initDirectoryListView();
|
initDirectoryListView();
|
||||||
init();
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -77,17 +71,6 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
|||||||
bind = null;
|
bind = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
|
||||||
Artist artist = getArguments().getParcelable(Constants.MUSIC_INDEX_OBJECT);
|
|
||||||
|
|
||||||
if (artist != null) {
|
|
||||||
directoryViewModel.setMusicDirectoryId(artist.getId());
|
|
||||||
directoryViewModel.setMusicDirectoryName(artist.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
directoryViewModel.loadMusicDirectory(getViewLifecycleOwner());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
activity.setSupportActionBar(bind.toolbar);
|
activity.setSupportActionBar(bind.toolbar);
|
||||||
|
|
||||||
@@ -96,39 +79,10 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
|||||||
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bind != null)
|
if (bind != null) {
|
||||||
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||||
|
bind.directoryBackImageView.setOnClickListener(v -> activity.navController.navigateUp());
|
||||||
if (bind != null)
|
}
|
||||||
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
|
||||||
if ((bind.directoryInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
|
||||||
directoryViewModel.getDirectory().observe(getViewLifecycleOwner(), directory -> {
|
|
||||||
if (directory != null) {
|
|
||||||
bind.toolbar.setTitle(directory.getName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
bind.toolbar.setTitle(R.string.empty_string);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
directoryViewModel.getDirectory().observe(getViewLifecycleOwner(), directory -> {
|
|
||||||
if (directory != null) {
|
|
||||||
bind.directoryTitleLabel.setText(directory.getName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initButtons() {
|
|
||||||
directoryViewModel.getDirectory().observe(getViewLifecycleOwner(), directory -> {
|
|
||||||
if (directory != null && directory.getParentId() != null && !Objects.equals(directory.getParentId(), "-1")) {
|
|
||||||
bind.directoryBackImageView.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
bind.directoryBackImageView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
bind.directoryBackImageView.setOnClickListener(v -> directoryViewModel.goBack());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDirectoryListView() {
|
private void initDirectoryListView() {
|
||||||
@@ -137,13 +91,18 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
musicDirectoryAdapter = new MusicDirectoryAdapter(this);
|
musicDirectoryAdapter = new MusicDirectoryAdapter(this);
|
||||||
bind.directoryRecyclerView.setAdapter(musicDirectoryAdapter);
|
bind.directoryRecyclerView.setAdapter(musicDirectoryAdapter);
|
||||||
|
directoryViewModel.loadMusicDirectory(getArguments().getString(Constants.MUSIC_DIRECTORY_ID)).observe(getViewLifecycleOwner(), directory -> {
|
||||||
|
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||||
|
if ((bind.directoryInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
||||||
|
bind.toolbar.setTitle(directory.getName());
|
||||||
|
} else {
|
||||||
|
bind.toolbar.setTitle(R.string.empty_string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
directoryViewModel.getDirectory().observe(getViewLifecycleOwner(), directory -> {
|
bind.directoryTitleLabel.setText(directory.getName());
|
||||||
if (directory != null) {
|
|
||||||
musicDirectoryAdapter.setItems(directory.getChildren());
|
musicDirectoryAdapter.setItems(directory.getChildren());
|
||||||
} else {
|
|
||||||
musicDirectoryAdapter.setItems(Collections.emptyList());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,11 +121,6 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMusicDirectoryClick(Bundle bundle) {
|
public void onMusicDirectoryClick(Bundle bundle) {
|
||||||
Child child = bundle.getParcelable(Constants.MUSIC_DIRECTORY_OBJECT);
|
Navigation.findNavController(requireView()).navigate(R.id.directoryFragment, bundle);
|
||||||
|
|
||||||
if (child != null) {
|
|
||||||
directoryViewModel.setMusicDirectoryId(child.getId());
|
|
||||||
directoryViewModel.setMusicDirectoryName(child.getTitle());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,6 @@ package com.cappielloantonio.tempo.ui.fragment;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
@@ -28,7 +25,7 @@ import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
|||||||
import com.cappielloantonio.tempo.ui.adapter.DownloadHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.DownloadHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.viewmodel.DownloadViewModel;
|
import com.cappielloantonio.tempo.viewmodel.DownloadViewModel;
|
||||||
import com.google.android.gms.cast.framework.CastButtonFactory;
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -43,18 +40,7 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
|
||||||
@Override
|
private MaterialToolbar materialToolbar;
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.main_page_menu, menu);
|
|
||||||
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.media_route_menu_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
@@ -97,22 +83,11 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||||||
bind = null;
|
bind = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.action_search) {
|
|
||||||
activity.navController.navigate(R.id.action_downloadFragment_to_searchFragment);
|
|
||||||
return true;
|
|
||||||
} else if (item.getItemId() == R.id.action_settings) {
|
|
||||||
activity.navController.navigate(R.id.action_downloadFragment_to_settingsFragment);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
activity.setSupportActionBar(bind.toolbar);
|
materialToolbar = bind.getRoot().findViewById(R.id.toolbar);
|
||||||
Objects.requireNonNull(bind.toolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
|
||||||
|
activity.setSupportActionBar(materialToolbar);
|
||||||
|
Objects.requireNonNull(materialToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDownloadedSongView() {
|
private void initDownloadedSongView() {
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ package com.cappielloantonio.tempo.ui.fragment;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
@@ -18,7 +15,9 @@ import com.cappielloantonio.tempo.databinding.FragmentHomeBinding;
|
|||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.fragment.pager.HomePager;
|
import com.cappielloantonio.tempo.ui.fragment.pager.HomePager;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
import com.google.android.gms.cast.framework.CastButtonFactory;
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
import com.google.android.material.tabs.TabLayoutMediator;
|
import com.google.android.material.tabs.TabLayoutMediator;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -30,18 +29,9 @@ public class HomeFragment extends Fragment {
|
|||||||
private FragmentHomeBinding bind;
|
private FragmentHomeBinding bind;
|
||||||
private MainActivity activity;
|
private MainActivity activity;
|
||||||
|
|
||||||
@Override
|
private MaterialToolbar materialToolbar;
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
private AppBarLayout appBarLayout;
|
||||||
super.onCreate(savedInstanceState);
|
private TabLayout tabLayout;
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.main_page_menu, menu);
|
|
||||||
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.media_route_menu_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
@@ -73,22 +63,18 @@ public class HomeFragment extends Fragment {
|
|||||||
bind = null;
|
bind = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.action_search) {
|
|
||||||
activity.navController.navigate(R.id.action_homeFragment_to_searchFragment);
|
|
||||||
return true;
|
|
||||||
} else if (item.getItemId() == R.id.action_settings) {
|
|
||||||
activity.navController.navigate(R.id.action_homeFragment_to_settingsFragment);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
activity.setSupportActionBar(bind.toolbar);
|
appBarLayout = bind.getRoot().findViewById(R.id.toolbar_fragment);
|
||||||
Objects.requireNonNull(bind.toolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
materialToolbar = bind.getRoot().findViewById(R.id.toolbar);
|
||||||
|
|
||||||
|
activity.setSupportActionBar(materialToolbar);
|
||||||
|
Objects.requireNonNull(materialToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
||||||
|
|
||||||
|
tabLayout = new TabLayout(requireContext());
|
||||||
|
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
|
||||||
|
tabLayout.setTabMode(TabLayout.MODE_FIXED);
|
||||||
|
|
||||||
|
appBarLayout.addView(tabLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initHomePager() {
|
private void initHomePager() {
|
||||||
@@ -106,13 +92,13 @@ public class HomeFragment extends Fragment {
|
|||||||
bind.homeViewPager.setOffscreenPageLimit(3);
|
bind.homeViewPager.setOffscreenPageLimit(3);
|
||||||
bind.homeViewPager.setUserInputEnabled(false);
|
bind.homeViewPager.setUserInputEnabled(false);
|
||||||
|
|
||||||
new TabLayoutMediator(bind.homeTabLayout, bind.homeViewPager,
|
new TabLayoutMediator(tabLayout, bind.homeViewPager,
|
||||||
(tab, position) -> {
|
(tab, position) -> {
|
||||||
tab.setText(pager.getPageTitle(position));
|
tab.setText(pager.getPageTitle(position));
|
||||||
// tab.setIcon(pager.getPageIcon(position));
|
// tab.setIcon(pager.getPageIcon(position));
|
||||||
}
|
}
|
||||||
).attach();
|
).attach();
|
||||||
|
|
||||||
bind.homeTabLayout.setVisibility(Preferences.isPodcastSectionVisible() || Preferences.isRadioSectionVisible() ? View.VISIBLE : View.GONE);
|
tabLayout.setVisibility(Preferences.isPodcastSectionVisible() || Preferences.isRadioSectionVisible() ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.os.Bundle;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -138,6 +139,15 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bind.discoveryTextViewClickable.setOnClickListener(v -> {
|
||||||
|
homeViewModel.getRandomShuffleSample().observe(getViewLifecycleOwner(), songs -> {
|
||||||
|
if (songs.size() > 0) {
|
||||||
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||||
|
activity.setBottomSheetInPeek(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
bind.similarTracksTextViewRefreshable.setOnLongClickListener(v -> {
|
bind.similarTracksTextViewRefreshable.setOnLongClickListener(v -> {
|
||||||
homeViewModel.refreshSimilarSongSample(getViewLifecycleOwner());
|
homeViewModel.refreshSimilarSongSample(getViewLifecycleOwner());
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ package com.cappielloantonio.tempo.ui.fragment;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
@@ -29,8 +26,9 @@ import com.cappielloantonio.tempo.ui.adapter.MusicFolderAdapter;
|
|||||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.PlaylistHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.PlaylistEditorDialog;
|
import com.cappielloantonio.tempo.ui.dialog.PlaylistEditorDialog;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
import com.cappielloantonio.tempo.viewmodel.LibraryViewModel;
|
import com.cappielloantonio.tempo.viewmodel.LibraryViewModel;
|
||||||
import com.google.android.gms.cast.framework.CastButtonFactory;
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -46,21 +44,9 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
private AlbumAdapter albumAdapter;
|
private AlbumAdapter albumAdapter;
|
||||||
private ArtistAdapter artistAdapter;
|
private ArtistAdapter artistAdapter;
|
||||||
private GenreAdapter genreAdapter;
|
private GenreAdapter genreAdapter;
|
||||||
|
|
||||||
private PlaylistHorizontalAdapter playlistHorizontalAdapter;
|
private PlaylistHorizontalAdapter playlistHorizontalAdapter;
|
||||||
|
|
||||||
@Override
|
private MaterialToolbar materialToolbar;
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.main_page_menu, menu);
|
|
||||||
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.media_route_menu_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
@@ -100,19 +86,6 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
bind = null;
|
bind = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.action_search) {
|
|
||||||
activity.navController.navigate(R.id.action_libraryFragment_to_searchFragment);
|
|
||||||
return true;
|
|
||||||
} else if (item.getItemId() == R.id.action_settings) {
|
|
||||||
activity.navController.navigate(R.id.action_libraryFragment_to_settingsFragment);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
bind.albumCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_albumCatalogueFragment));
|
bind.albumCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_albumCatalogueFragment));
|
||||||
bind.artistCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_artistCatalogueFragment));
|
bind.artistCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_artistCatalogueFragment));
|
||||||
@@ -142,11 +115,18 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initAppBar() {
|
private void initAppBar() {
|
||||||
activity.setSupportActionBar(bind.toolbar);
|
materialToolbar = bind.getRoot().findViewById(R.id.toolbar);
|
||||||
Objects.requireNonNull(bind.toolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
|
||||||
|
activity.setSupportActionBar(materialToolbar);
|
||||||
|
Objects.requireNonNull(materialToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initMusicFolderView() {
|
private void initMusicFolderView() {
|
||||||
|
if (!Preferences.isMusicDirectorySectionVisible()) {
|
||||||
|
bind.libraryMusicFolderSector.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bind.musicFolderRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.musicFolderRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.musicFolderRecyclerView.setHasFixedSize(true);
|
bind.musicFolderRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
|||||||
import com.cappielloantonio.tempo.service.MediaManager;
|
import com.cappielloantonio.tempo.service.MediaManager;
|
||||||
import com.cappielloantonio.tempo.service.MediaService;
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.PlayQueue;
|
import com.cappielloantonio.tempo.subsonic.models.PlayQueue;
|
||||||
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.fragment.pager.PlayerControllerVerticalPager;
|
import com.cappielloantonio.tempo.ui.fragment.pager.PlayerControllerVerticalPager;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
@@ -57,6 +58,7 @@ public class PlayerBottomSheetFragment extends Fragment {
|
|||||||
playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
|
playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
|
||||||
|
|
||||||
customizeBottomSheetBackground();
|
customizeBottomSheetBackground();
|
||||||
|
customizeBottomSheetAction();
|
||||||
initViewPager();
|
initViewPager();
|
||||||
setHeaderBookmarksButton();
|
setHeaderBookmarksButton();
|
||||||
|
|
||||||
@@ -87,6 +89,10 @@ public class PlayerBottomSheetFragment extends Fragment {
|
|||||||
bind.playerHeaderLayout.getRoot().setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 2));
|
bind.playerHeaderLayout.getRoot().setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void customizeBottomSheetAction() {
|
||||||
|
bind.playerHeaderLayout.getRoot().setOnClickListener(view -> ((MainActivity) requireActivity()).expandBottomSheet());
|
||||||
|
}
|
||||||
|
|
||||||
private void initViewPager() {
|
private void initViewPager() {
|
||||||
bind.playerBodyLayout.playerBodyBottomSheetViewPager.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
|
bind.playerBodyLayout.playerBodyBottomSheetViewPager.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
|
||||||
bind.playerBodyLayout.playerBodyBottomSheetViewPager.setAdapter(new PlayerControllerVerticalPager(this));
|
bind.playerBodyLayout.playerBodyBottomSheetViewPager.setAdapter(new PlayerControllerVerticalPager(this));
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ public class SearchFragment extends Fragment implements ClickCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void performSearch(String query) {
|
private void performSearch(String query) {
|
||||||
searchViewModel.search(query).observe(getViewLifecycleOwner(), result -> {
|
searchViewModel.search3(query).observe(getViewLifecycleOwner(), result -> {
|
||||||
if (bind != null) {
|
if (bind != null) {
|
||||||
if (result.getArtists() != null) {
|
if (result.getArtists() != null) {
|
||||||
bind.searchArtistSector.setVisibility(!result.getArtists().isEmpty() ? View.VISIBLE : View.GONE);
|
bind.searchArtistSector.setVisibility(!result.getArtists().isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.media3.common.util.UnstableApi;
|
|||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.BuildConfig;
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
||||||
import com.cappielloantonio.tempo.interfaces.ScanCallback;
|
import com.cappielloantonio.tempo.interfaces.ScanCallback;
|
||||||
@@ -69,6 +70,8 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
|
findPreference("version").setSummary(BuildConfig.VERSION_NAME);
|
||||||
|
|
||||||
findPreference("logout").setOnPreferenceClickListener(preference -> {
|
findPreference("logout").setOnPreferenceClickListener(preference -> {
|
||||||
activity.quit();
|
activity.quit();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ object Constants {
|
|||||||
const val MUSIC_FOLDER_OBJECT = "MUSIC_FOLDER_OBJECT"
|
const val MUSIC_FOLDER_OBJECT = "MUSIC_FOLDER_OBJECT"
|
||||||
const val MUSIC_DIRECTORY_OBJECT = "MUSIC_DIRECTORY_OBJECT"
|
const val MUSIC_DIRECTORY_OBJECT = "MUSIC_DIRECTORY_OBJECT"
|
||||||
const val MUSIC_INDEX_OBJECT = "MUSIC_DIRECTORY_OBJECT"
|
const val MUSIC_INDEX_OBJECT = "MUSIC_DIRECTORY_OBJECT"
|
||||||
|
const val MUSIC_DIRECTORY_ID = "MUSIC_DIRECTORY_ID"
|
||||||
|
|
||||||
const val ALBUM_RECENTLY_PLAYED = "ALBUM_RECENTLY_PLAYED"
|
const val ALBUM_RECENTLY_PLAYED = "ALBUM_RECENTLY_PLAYED"
|
||||||
const val ALBUM_MOST_PLAYED = "ALBUM_MOST_PLAYED"
|
const val ALBUM_MOST_PLAYED = "ALBUM_MOST_PLAYED"
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ object Preferences {
|
|||||||
private const val ROUNDED_CORNER_SIZE = "rounded_corner_size"
|
private const val ROUNDED_CORNER_SIZE = "rounded_corner_size"
|
||||||
private const val PODCAST_SECTION_VISIBILITY = "podcast_section_visibility"
|
private const val PODCAST_SECTION_VISIBILITY = "podcast_section_visibility"
|
||||||
private const val RADIO_SECTION_VISIBILITY = "radio_section_visibility"
|
private const val RADIO_SECTION_VISIBILITY = "radio_section_visibility"
|
||||||
|
private const val MUSIC_DIRECTORY_SECTION_VISIBILITY = "music_directory_section_visibility"
|
||||||
|
private const val REPLAY_GAIN_MODE = "replay_gain_mode"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getServer(): String? {
|
fun getServer(): String? {
|
||||||
@@ -240,4 +242,14 @@ object Preferences {
|
|||||||
fun setRadioSectionHidden() {
|
fun setRadioSectionHidden() {
|
||||||
App.getInstance().preferences.edit().putBoolean(RADIO_SECTION_VISIBILITY, false).apply()
|
App.getInstance().preferences.edit().putBoolean(RADIO_SECTION_VISIBILITY, false).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isMusicDirectorySectionVisible(): Boolean {
|
||||||
|
return App.getInstance().preferences.getBoolean(MUSIC_DIRECTORY_SECTION_VISIBILITY, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getReplayGainMode(): String? {
|
||||||
|
return App.getInstance().preferences.getString(REPLAY_GAIN_MODE, "disabled")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package com.cappielloantonio.tempo.util;
|
||||||
|
|
||||||
|
import androidx.media3.common.Metadata;
|
||||||
|
import androidx.media3.common.Tracks;
|
||||||
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.model.ReplayGain;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ReplayGainUtil {
|
||||||
|
private static final String[] tags = {"REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN", "R128_TRACK_GAIN", "R128_ALBUM_GAIN"};
|
||||||
|
|
||||||
|
public static void setReplayGain(ExoPlayer player, Tracks tracks) {
|
||||||
|
List<Metadata> metadata = getMetadata(tracks);
|
||||||
|
List<ReplayGain> gains = getReplayGains(metadata);
|
||||||
|
|
||||||
|
applyReplayGain(player, gains);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Metadata> getMetadata(Tracks tracks) {
|
||||||
|
List<Metadata> metadata = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < tracks.getGroups().size(); i++) {
|
||||||
|
Tracks.Group group = tracks.getGroups().get(i);
|
||||||
|
|
||||||
|
for (int j = 0; j < group.getMediaTrackGroup().length; j++) {
|
||||||
|
metadata.add(group.getTrackFormat(j).metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ReplayGain> getReplayGains(List<Metadata> metadata) {
|
||||||
|
List<ReplayGain> gains = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < metadata.size(); i++) {
|
||||||
|
for (int j = 0; j < metadata.get(i).length(); j++) {
|
||||||
|
Metadata.Entry entry = metadata.get(i).get(j);
|
||||||
|
|
||||||
|
if (checkReplayGain(entry)) {
|
||||||
|
ReplayGain replayGain = setReplayGains(entry);
|
||||||
|
gains.add(replayGain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gains;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkReplayGain(Metadata.Entry entry) {
|
||||||
|
for (String tag : tags) {
|
||||||
|
if (entry.toString().contains(tag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReplayGain setReplayGains(Metadata.Entry entry) {
|
||||||
|
ReplayGain replayGain = new ReplayGain();
|
||||||
|
|
||||||
|
if (entry.toString().contains(tags[0])) {
|
||||||
|
replayGain.setTrackGain(parseReplayGainTag(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.toString().contains(tags[1])) {
|
||||||
|
replayGain.setAlbumGain(parseReplayGainTag(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.toString().contains(tags[2])) {
|
||||||
|
replayGain.setTrackGain(parseReplayGainTag(entry) / 256f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.toString().contains(tags[3])) {
|
||||||
|
replayGain.setAlbumGain(parseReplayGainTag(entry) / 256f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return replayGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Float parseReplayGainTag(Metadata.Entry entry) {
|
||||||
|
try {
|
||||||
|
return Float.parseFloat(entry.toString().replaceAll("[^\\d.]", ""));
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyReplayGain(ExoPlayer player, List<ReplayGain> gains) {
|
||||||
|
if (Objects.equals(Preferences.getReplayGainMode(), "disabled") || gains.size() == 0) {
|
||||||
|
setReplayGain(player, 0f);
|
||||||
|
} else if (Objects.equals(Preferences.getReplayGainMode(), "track")) {
|
||||||
|
setReplayGain(player, gains.get(0).getTrackGain() != 0f ? gains.get(0).getTrackGain() : gains.get(0).getAlbumGain());
|
||||||
|
} else if (Objects.equals(Preferences.getReplayGainMode(), "album")) {
|
||||||
|
setReplayGain(player, gains.get(0).getAlbumGain() != 0f ? gains.get(0).getAlbumGain() : gains.get(0).getTrackGain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setReplayGain(ExoPlayer player, float gain) {
|
||||||
|
player.setVolume((float) Math.pow(10f, gain / 20f));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,9 +7,6 @@ import android.graphics.drawable.InsetDrawable;
|
|||||||
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
|
|
||||||
import com.google.android.gms.common.ConnectionResult;
|
|
||||||
import com.google.android.gms.common.GoogleApiAvailability;
|
|
||||||
|
|
||||||
public class UIUtil {
|
public class UIUtil {
|
||||||
public static int getSpanCount(int itemCount, int maxSpan) {
|
public static int getSpanCount(int itemCount, int maxSpan) {
|
||||||
int itemSize = itemCount == 0 ? 1 : itemCount;
|
int itemSize = itemCount == 0 ? 1 : itemCount;
|
||||||
@@ -21,10 +18,6 @@ public class UIUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isCastApiAvailable(Context context) {
|
|
||||||
return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DividerItemDecoration getDividerItemDecoration(Context context) {
|
public static DividerItemDecoration getDividerItemDecoration(Context context) {
|
||||||
int[] ATTRS = new int[]{android.R.attr.listDivider};
|
int[] ATTRS = new int[]{android.R.attr.listDivider};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import android.app.Application;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.repository.DirectoryRepository;
|
import com.cappielloantonio.tempo.repository.DirectoryRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Directory;
|
import com.cappielloantonio.tempo.subsonic.models.Directory;
|
||||||
@@ -14,34 +12,13 @@ import com.cappielloantonio.tempo.subsonic.models.Directory;
|
|||||||
public class DirectoryViewModel extends AndroidViewModel {
|
public class DirectoryViewModel extends AndroidViewModel {
|
||||||
private final DirectoryRepository directoryRepository;
|
private final DirectoryRepository directoryRepository;
|
||||||
|
|
||||||
private MutableLiveData<String> id = new MutableLiveData<>(null);
|
|
||||||
private MutableLiveData<String> name = new MutableLiveData<>(null);
|
|
||||||
|
|
||||||
private MutableLiveData<Directory> directory = new MutableLiveData<>(null);
|
|
||||||
|
|
||||||
public DirectoryViewModel(@NonNull Application application) {
|
public DirectoryViewModel(@NonNull Application application) {
|
||||||
super(application);
|
super(application);
|
||||||
|
|
||||||
directoryRepository = new DirectoryRepository();
|
directoryRepository = new DirectoryRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Directory> getDirectory() {
|
public LiveData<Directory> loadMusicDirectory(String id) {
|
||||||
return directory;
|
return directoryRepository.getMusicDirectory(id);
|
||||||
}
|
|
||||||
|
|
||||||
public void setMusicDirectoryId(String id) {
|
|
||||||
this.id.setValue(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMusicDirectoryName(String name) {
|
|
||||||
this.name.setValue(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadMusicDirectory(LifecycleOwner owner) {
|
|
||||||
this.id.observe(owner, id -> directoryRepository.getMusicDirectory(id).observe(owner, directory -> this.directory.setValue(directory)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void goBack() {
|
|
||||||
this.id.setValue(this.directory.getValue().getParentId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ public class HomeViewModel extends AndroidViewModel {
|
|||||||
return dicoverSongSample;
|
return dicoverSongSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Child>> getRandomShuffleSample() {
|
||||||
|
return songRepository.getRandomSample(100, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public LiveData<List<Chronology>> getGridSongSample(LifecycleOwner owner) {
|
public LiveData<List<Chronology>> getGridSongSample(LifecycleOwner owner) {
|
||||||
String server = Preferences.getServerId();
|
String server = Preferences.getServerId();
|
||||||
chronologyRepository.getLastWeek(server).observe(owner, thisGridTopSong::postValue);
|
chronologyRepository.getLastWeek(server).observe(owner, thisGridTopSong::postValue);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.lifecycle.LiveData;
|
|||||||
|
|
||||||
import com.cappielloantonio.tempo.model.RecentSearch;
|
import com.cappielloantonio.tempo.model.RecentSearch;
|
||||||
import com.cappielloantonio.tempo.repository.SearchingRepository;
|
import com.cappielloantonio.tempo.repository.SearchingRepository;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.SearchResult2;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult3;
|
import com.cappielloantonio.tempo.subsonic.models.SearchResult3;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -38,8 +39,12 @@ public class SearchViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<SearchResult3> search(String title) {
|
public LiveData<SearchResult2> search2(String title) {
|
||||||
return searchingRepository.search(title);
|
return searchingRepository.search2(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<SearchResult3> search3(String title) {
|
||||||
|
return searchingRepository.search3(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertNewSearch(String search) {
|
public void insertNewSearch(String search) {
|
||||||
|
|||||||
@@ -188,6 +188,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingTop="8dp" />
|
android:paddingTop="8dp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -4,40 +4,11 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<fragment
|
||||||
android:id="@+id/appbar"
|
android:id="@+id/toolbar_fragment"
|
||||||
|
android:name="com.cappielloantonio.tempo.ui.fragment.ToolbarFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/colorSurface"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways|snap">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="28dp"
|
|
||||||
android:layout_height="28dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="@drawable/ic_toolbar_tempo" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/HeadlineMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:text="@string/app_name" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.appbar.MaterialToolbar>
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/loading_progress_bar"
|
android:id="@+id/loading_progress_bar"
|
||||||
@@ -121,6 +92,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
android:paddingBottom="8dp" />
|
android:paddingBottom="8dp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -6,47 +6,11 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.fragment.HomeFragment">
|
tools:context=".ui.fragment.HomeFragment">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<fragment
|
||||||
android:id="@+id/appbar"
|
android:id="@+id/toolbar_fragment"
|
||||||
|
android:name="com.cappielloantonio.tempo.ui.fragment.ToolbarFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/colorSurface"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways|snap">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="28dp"
|
|
||||||
android:layout_height="28dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="@drawable/ic_toolbar_tempo" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/HeadlineMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:text="@string/app_name" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.appbar.MaterialToolbar>
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
|
||||||
android:id="@+id/homeTabLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:tabGravity="fill"
|
|
||||||
app:tabMode="fixed" />
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/homeViewPager"
|
android:id="@+id/homeViewPager"
|
||||||
|
|||||||
@@ -113,16 +113,35 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<!-- Label and button -->
|
||||||
android:id="@+id/discovery_text_view_refreshable"
|
<LinearLayout
|
||||||
style="@style/TitleLarge"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="8dp"
|
||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="8dp">
|
||||||
android:text="@string/home_title_discovery" />
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/discovery_text_view_refreshable"
|
||||||
|
style="@style/TitleLarge"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:text="@string/home_title_discovery" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/discovery_text_view_clickable"
|
||||||
|
style="@style/TitleMedium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:text="@string/home_title_discovery_shuffle_all_button" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- slideview -->
|
<!-- slideview -->
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
|||||||
@@ -4,40 +4,11 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<fragment
|
||||||
android:id="@+id/appbar"
|
android:id="@+id/toolbar_fragment"
|
||||||
|
android:name="com.cappielloantonio.tempo.ui.fragment.ToolbarFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/colorSurface"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways|snap">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="28dp"
|
|
||||||
android:layout_height="28dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="@drawable/ic_toolbar_tempo" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/HeadlineMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:text="@string/app_name" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.appbar.MaterialToolbar>
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/fragment_library_nested_scroll_view"
|
android:id="@+id/fragment_library_nested_scroll_view"
|
||||||
@@ -75,6 +46,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
@@ -308,6 +280,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:paddingBottom="8dp" />
|
android:paddingBottom="8dp" />
|
||||||
|
|||||||
36
app/src/main/res/layout/fragment_toolbar.xml
Normal file
36
app/src/main/res/layout/fragment_toolbar.xml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.appbar.AppBarLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/appbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways|snap">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="@drawable/ic_toolbar_tempo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/HeadlineMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:text="@string/app_name" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.appbar.MaterialToolbar>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
36
app/src/main/res/layout/layout_toolbar.xml
Normal file
36
app/src/main/res/layout/layout_toolbar.xml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.appbar.AppBarLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/appbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways|snap">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="@drawable/ic_toolbar_tempo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/HeadlineMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:text="@string/app_name" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.appbar.MaterialToolbar>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
@@ -286,13 +286,16 @@
|
|||||||
android:id="@+id/directoryFragment"
|
android:id="@+id/directoryFragment"
|
||||||
android:name="com.cappielloantonio.tempo.ui.fragment.DirectoryFragment"
|
android:name="com.cappielloantonio.tempo.ui.fragment.DirectoryFragment"
|
||||||
android:label="DirectoryFragment"
|
android:label="DirectoryFragment"
|
||||||
tools:layout="@layout/fragment_directory" />
|
tools:layout="@layout/fragment_directory">
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_directoryFragment_to_directoryFragment"
|
||||||
|
app:destination="@id/directoryFragment" />
|
||||||
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/indexFragment"
|
android:id="@+id/indexFragment"
|
||||||
android:name="com.cappielloantonio.tempo.ui.fragment.IndexFragment"
|
android:name="com.cappielloantonio.tempo.ui.fragment.IndexFragment"
|
||||||
android:label="IndexFragment"
|
android:label="IndexFragment"
|
||||||
tools:layout="@layout/fragment_index">
|
tools:layout="@layout/fragment_index">
|
||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_indexFragment_to_directoryFragment"
|
android:id="@+id/action_indexFragment_to_directoryFragment"
|
||||||
app:destination="@id/directoryFragment" />
|
app:destination="@id/directoryFragment" />
|
||||||
|
|||||||
@@ -141,4 +141,15 @@
|
|||||||
<item>12</item>
|
<item>12</item>
|
||||||
<item>6</item>
|
<item>6</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="replay_gain_titles">
|
||||||
|
<item>Disabled</item>
|
||||||
|
<item>Track preferred</item>
|
||||||
|
<item>Album preferred</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="replay_gain_values">
|
||||||
|
<item>disabled</item>
|
||||||
|
<item>track</item>
|
||||||
|
<item>album</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -77,6 +77,7 @@
|
|||||||
<string name="home_title_discovery">Discovery</string>
|
<string name="home_title_discovery">Discovery</string>
|
||||||
<string name="home_title_recently_added">Recently added</string>
|
<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_recently_added_see_all_button">See all</string>
|
||||||
|
<string name="home_title_discovery_shuffle_all_button">Shuffle all</string>
|
||||||
<string name="home_title_starred_albums">★ Starred albums</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_albums_see_all_button">See all</string>
|
||||||
<string name="home_title_starred_artists">★ Starred artists</string>
|
<string name="home_title_starred_artists">★ Starred artists</string>
|
||||||
@@ -184,6 +185,8 @@
|
|||||||
<string name="settings_max_bitrate_wifi">Bitrate in Wi-Fi</string>
|
<string name="settings_max_bitrate_wifi">Bitrate in Wi-Fi</string>
|
||||||
<string name="settings_max_bitrate_mobile">Bitrate in mobile</string>
|
<string name="settings_max_bitrate_mobile">Bitrate in mobile</string>
|
||||||
<string name="settings_media_cache">Size of media file cache</string>
|
<string name="settings_media_cache">Size of media file cache</string>
|
||||||
|
<string name="settings_music_directory">Show music directories</string>
|
||||||
|
<string name="settings_music_directory_summary">If enabled, show the music directory section. Please note that for folder navigation to work properly, the server must support this feature.</string>
|
||||||
<string name="settings_queue_syncing_title">Sync play queue for this user</string>
|
<string name="settings_queue_syncing_title">Sync play queue for this user</string>
|
||||||
<string name="settings_queue_syncing_countdown">Sync timer</string>
|
<string name="settings_queue_syncing_countdown">Sync timer</string>
|
||||||
<string name="settings_queue_syncing_summary">If enabled, the user will have the ability to save their play queue and will have the ability to load state when opening the application.</string>
|
<string name="settings_queue_syncing_summary">If enabled, the user will have the ability to save their play queue and will have the ability to load state when opening the application.</string>
|
||||||
@@ -191,11 +194,13 @@
|
|||||||
<string name="settings_podcast_summary">If enabled, show the podcast section.</string>
|
<string name="settings_podcast_summary">If enabled, show the podcast section.</string>
|
||||||
<string name="settings_radio">Show radio</string>
|
<string name="settings_radio">Show radio</string>
|
||||||
<string name="settings_radio_summary">If enabled, show the radio section.</string>
|
<string name="settings_radio_summary">If enabled, show the radio section.</string>
|
||||||
|
<string name="settings_replay_gain">Set replay gain mode</string>
|
||||||
<string name="settings_rounded_corner">Rounded corners</string>
|
<string name="settings_rounded_corner">Rounded corners</string>
|
||||||
<string name="settings_rounded_corner_summary">If enabled, sets a curvature angle for all rendered covers. The changes will take effect on restart.</string>
|
<string name="settings_rounded_corner_summary">If enabled, sets a curvature angle for all rendered covers. The changes will take effect on restart.</string>
|
||||||
<string name="settings_rounded_corner_size">Corners size</string>
|
<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_rounded_corner_size_summary">Sets the magnitude of the curvature angle.</string>
|
||||||
<string name="settings_scan_title">Scan library</string>
|
<string name="settings_scan_title">Scan library</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_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_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">Priority given to the transcoding mode. If 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_summary">If enabled, starred tracks will be downloaded for offline use.</string>
|
||||||
@@ -203,6 +208,7 @@
|
|||||||
<string name="settings_theme">Theme</string>
|
<string name="settings_theme">Theme</string>
|
||||||
<string name="settings_title_data">Data</string>
|
<string name="settings_title_data">Data</string>
|
||||||
<string name="settings_title_general">General</string>
|
<string name="settings_title_general">General</string>
|
||||||
|
<string name="settings_title_replay_gain">Replay Gain</string>
|
||||||
<string name="settings_title_syncing">Syncing</string>
|
<string name="settings_title_syncing">Syncing</string>
|
||||||
<string name="settings_title_transcoding">Transcoding</string>
|
<string name="settings_title_transcoding">Transcoding</string>
|
||||||
<string name="settings_title_ui">UI</string>
|
<string name="settings_title_ui">UI</string>
|
||||||
@@ -255,4 +261,6 @@
|
|||||||
<string name="menu_sort_name">Name</string>
|
<string name="menu_sort_name">Name</string>
|
||||||
<string name="menu_sort_random">Random</string>
|
<string name="menu_sort_random">Random</string>
|
||||||
<string name="description_empty_title">No description available</string>
|
<string name="description_empty_title">No description available</string>
|
||||||
|
<!-- TODO: Remove or change this placeholder text -->
|
||||||
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -51,6 +51,12 @@
|
|||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:summary="@string/settings_radio_summary"
|
android:summary="@string/settings_radio_summary"
|
||||||
android:key="radio_section_visibility" />
|
android:key="radio_section_visibility" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:title="@string/settings_music_directory"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:summary="@string/settings_music_directory_summary"
|
||||||
|
android:key="music_directory_section_visibility" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory app:title="@string/settings_title_data">
|
<PreferenceCategory app:title="@string/settings_title_data">
|
||||||
@@ -133,6 +139,22 @@
|
|||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory app:title="@string/settings_title_replay_gain">
|
||||||
|
<Preference
|
||||||
|
app:selectable="false"
|
||||||
|
app:summary="@string/settings_summary_replay_gain" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
app:defaultValue="disabled"
|
||||||
|
app:dialogTitle="@string/settings_replay_gain"
|
||||||
|
app:entries="@array/replay_gain_titles"
|
||||||
|
app:entryValues="@array/replay_gain_values"
|
||||||
|
app:key="replay_gain_mode"
|
||||||
|
app:title="@string/settings_replay_gain"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory app:title="@string/settings_title_syncing">
|
<PreferenceCategory app:title="@string/settings_title_syncing">
|
||||||
<Preference
|
<Preference
|
||||||
app:selectable="false"
|
app:selectable="false"
|
||||||
@@ -141,7 +163,7 @@
|
|||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:title="@string/settings_queue_syncing_title"
|
android:title="@string/settings_queue_syncing_title"
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:summary="@string/settings_sync_starred_tracks_for_offline_use_summary"
|
android:summary="@string/settings_queue_syncing_summary"
|
||||||
android:key="queue_syncing" />
|
android:key="queue_syncing" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
@@ -160,6 +182,7 @@
|
|||||||
app:summary="@string/settings_about_summary" />
|
app:summary="@string/settings_about_summary" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
android:key="version"
|
||||||
app:summary="@string/settings_version_summary"
|
app:summary="@string/settings_version_summary"
|
||||||
app:title="@string/settings_version_title" />
|
app:title="@string/settings_version_title" />
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
package com.cappielloantonio.tempo.service
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||||
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
import android.app.TaskStackBuilder
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.media3.common.*
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelectionArray
|
||||||
|
import androidx.media3.session.*
|
||||||
|
import androidx.media3.session.MediaSession.ControllerInfo
|
||||||
|
import com.cappielloantonio.tempo.R
|
||||||
|
import com.cappielloantonio.tempo.ui.activity.MainActivity
|
||||||
|
import com.cappielloantonio.tempo.util.Constants
|
||||||
|
import com.cappielloantonio.tempo.util.DownloadUtil
|
||||||
|
import com.cappielloantonio.tempo.util.ReplayGainUtil
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.google.common.util.concurrent.Futures
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
|
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
class MediaService : MediaLibraryService() {
|
||||||
|
private val librarySessionCallback = CustomMediaLibrarySessionCallback()
|
||||||
|
|
||||||
|
private lateinit var player: ExoPlayer
|
||||||
|
private lateinit var mediaLibrarySession: MediaLibrarySession
|
||||||
|
private lateinit var customCommands: List<CommandButton>
|
||||||
|
|
||||||
|
private var customLayout = ImmutableList.of<CommandButton>()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON =
|
||||||
|
"android.media3.session.demo.SHUFFLE_ON"
|
||||||
|
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF =
|
||||||
|
"android.media3.session.demo.SHUFFLE_OFF"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
initializeCustomCommands()
|
||||||
|
initializePlayer()
|
||||||
|
initializeMediaLibrarySession()
|
||||||
|
initializePlayerListener()
|
||||||
|
|
||||||
|
setPlayer(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetSession(controllerInfo: ControllerInfo): MediaLibrarySession {
|
||||||
|
return mediaLibrarySession
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
releasePlayer()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class CustomMediaLibrarySessionCallback : MediaLibrarySession.Callback {
|
||||||
|
|
||||||
|
override fun onConnect(
|
||||||
|
session: MediaSession,
|
||||||
|
controller: ControllerInfo
|
||||||
|
): MediaSession.ConnectionResult {
|
||||||
|
val connectionResult = super.onConnect(session, controller)
|
||||||
|
val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon()
|
||||||
|
|
||||||
|
customCommands.forEach { commandButton ->
|
||||||
|
// TODO: Aggiungere i comandi personalizzati
|
||||||
|
// commandButton.sessionCommand?.let { availableSessionCommands.add(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return MediaSession.ConnectionResult.accept(
|
||||||
|
availableSessionCommands.build(),
|
||||||
|
connectionResult.availablePlayerCommands
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostConnect(session: MediaSession, controller: ControllerInfo) {
|
||||||
|
if (!customLayout.isEmpty() && controller.controllerVersion != 0) {
|
||||||
|
ignoreFuture(mediaLibrarySession.setCustomLayout(controller, customLayout))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCustomCommand(
|
||||||
|
session: MediaSession,
|
||||||
|
controller: ControllerInfo,
|
||||||
|
customCommand: SessionCommand,
|
||||||
|
args: Bundle
|
||||||
|
): ListenableFuture<SessionResult> {
|
||||||
|
if (CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON == customCommand.customAction) {
|
||||||
|
player.shuffleModeEnabled = true
|
||||||
|
customLayout = ImmutableList.of(customCommands[1])
|
||||||
|
session.setCustomLayout(customLayout)
|
||||||
|
} else if (CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF == customCommand.customAction) {
|
||||||
|
player.shuffleModeEnabled = false
|
||||||
|
customLayout = ImmutableList.of(customCommands[0])
|
||||||
|
session.setCustomLayout(customLayout)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAddMediaItems(
|
||||||
|
mediaSession: MediaSession,
|
||||||
|
controller: ControllerInfo,
|
||||||
|
mediaItems: List<MediaItem>
|
||||||
|
): ListenableFuture<List<MediaItem>> {
|
||||||
|
val updatedMediaItems = mediaItems.map {
|
||||||
|
it.buildUpon()
|
||||||
|
.setUri(it.requestMetadata.mediaUri)
|
||||||
|
.setMediaMetadata(it.mediaMetadata)
|
||||||
|
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
return Futures.immediateFuture(updatedMediaItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeCustomCommands() {
|
||||||
|
customCommands =
|
||||||
|
listOf(
|
||||||
|
getShuffleCommandButton(
|
||||||
|
SessionCommand(CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON, Bundle.EMPTY)
|
||||||
|
),
|
||||||
|
getShuffleCommandButton(
|
||||||
|
SessionCommand(CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF, Bundle.EMPTY)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
customLayout = ImmutableList.of(customCommands[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializePlayer() {
|
||||||
|
player = ExoPlayer.Builder(this)
|
||||||
|
.setRenderersFactory(getRenderersFactory())
|
||||||
|
.setMediaSourceFactory(getMediaSourceFactory())
|
||||||
|
.setAudioAttributes(AudioAttributes.DEFAULT, true)
|
||||||
|
.setHandleAudioBecomingNoisy(true)
|
||||||
|
.setWakeMode(C.WAKE_MODE_NETWORK)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeMediaLibrarySession() {
|
||||||
|
val sessionActivityPendingIntent =
|
||||||
|
TaskStackBuilder.create(this).run {
|
||||||
|
addNextIntent(Intent(this@MediaService, MainActivity::class.java))
|
||||||
|
getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaLibrarySession =
|
||||||
|
MediaLibrarySession.Builder(this, player, librarySessionCallback)
|
||||||
|
.setSessionActivity(sessionActivityPendingIntent)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
if (!customLayout.isEmpty()) {
|
||||||
|
mediaLibrarySession.setCustomLayout(customLayout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializePlayerListener() {
|
||||||
|
player.addListener(object : Player.Listener {
|
||||||
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
||||||
|
if (mediaItem == null) return
|
||||||
|
|
||||||
|
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_SEEK || reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
||||||
|
MediaManager.setLastPlayedTimestamp(mediaItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTracksChanged(tracks: Tracks) {
|
||||||
|
ReplayGainUtil.setReplayGain(player, tracks)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
|
if (!isPlaying) {
|
||||||
|
MediaManager.setPlayingPausedTimestamp(
|
||||||
|
player.currentMediaItem,
|
||||||
|
player.currentPosition
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPositionDiscontinuity(
|
||||||
|
oldPosition: Player.PositionInfo,
|
||||||
|
newPosition: Player.PositionInfo,
|
||||||
|
reason: Int
|
||||||
|
) {
|
||||||
|
super.onPositionDiscontinuity(oldPosition, newPosition, reason)
|
||||||
|
|
||||||
|
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||||
|
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||||
|
MediaManager.scrobble(oldPosition.mediaItem)
|
||||||
|
MediaManager.saveChronology(oldPosition.mediaItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||||
|
MediaManager.setLastPlayedTimestamp(newPosition.mediaItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setPlayer(player: Player) {
|
||||||
|
mediaLibrarySession.player = player
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun releasePlayer() {
|
||||||
|
player.release()
|
||||||
|
mediaLibrarySession.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("PrivateResource")
|
||||||
|
private fun getShuffleCommandButton(sessionCommand: SessionCommand): CommandButton {
|
||||||
|
val isOn = sessionCommand.customAction == CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON
|
||||||
|
return CommandButton.Builder()
|
||||||
|
.setDisplayName(
|
||||||
|
getString(
|
||||||
|
if (isOn) R.string.exo_controls_shuffle_on_description
|
||||||
|
else R.string.exo_controls_shuffle_off_description
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setSessionCommand(sessionCommand)
|
||||||
|
.setIconResId(if (isOn) R.drawable.exo_icon_shuffle_off else R.drawable.exo_icon_shuffle_on)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ignoreFuture(customLayout: ListenableFuture<SessionResult>) {
|
||||||
|
/* Do nothing. */
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false)
|
||||||
|
|
||||||
|
private fun getMediaSourceFactory() =
|
||||||
|
DefaultMediaSourceFactory(this).setDataSourceFactory(DownloadUtil.getDataSourceFactory(this))
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
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 androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
|
import com.cappielloantonio.tempo.databinding.FragmentToolbarBinding;
|
||||||
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
public class ToolbarFragment extends Fragment {
|
||||||
|
private static final String TAG = "ToolbarFragment";
|
||||||
|
|
||||||
|
private FragmentToolbarBinding bind;
|
||||||
|
private MainActivity activity;
|
||||||
|
|
||||||
|
public ToolbarFragment() {
|
||||||
|
// Required empty public constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.main_page_menu, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
activity = (MainActivity) getActivity();
|
||||||
|
|
||||||
|
bind = FragmentToolbarBinding.inflate(inflater, container, false);
|
||||||
|
View view = bind.getRoot();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_search) {
|
||||||
|
activity.navController.navigate(R.id.searchFragment);
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_settings) {
|
||||||
|
activity.navController.navigate(R.id.settingsFragment);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.cappielloantonio.tempo.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
public class Flavors {
|
||||||
|
public static void initializeCastContext(Context context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
15
app/src/notquitemy/res/menu/main_page_menu.xml
Normal file
15
app/src/notquitemy/res/menu/main_page_menu.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_search"
|
||||||
|
android:icon="@drawable/ic_search"
|
||||||
|
android:title="@string/menu_search_button"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_settings"
|
||||||
|
android:icon="@drawable/ic_settings"
|
||||||
|
android:title="@string/menu_settings_button"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
||||||
@@ -18,8 +18,10 @@ import com.cappielloantonio.tempo.R
|
|||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity
|
import com.cappielloantonio.tempo.ui.activity.MainActivity
|
||||||
import com.cappielloantonio.tempo.util.Constants
|
import com.cappielloantonio.tempo.util.Constants
|
||||||
import com.cappielloantonio.tempo.util.DownloadUtil
|
import com.cappielloantonio.tempo.util.DownloadUtil
|
||||||
import com.cappielloantonio.tempo.util.UIUtil
|
import com.cappielloantonio.tempo.util.ReplayGainUtil
|
||||||
import com.google.android.gms.cast.framework.CastContext
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
|
import com.google.android.gms.common.ConnectionResult
|
||||||
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.util.concurrent.Futures
|
import com.google.common.util.concurrent.Futures
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
@@ -117,6 +119,9 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
|||||||
browser: ControllerInfo,
|
browser: ControllerInfo,
|
||||||
params: LibraryParams?
|
params: LibraryParams?
|
||||||
): ListenableFuture<LibraryResult<MediaItem>> {
|
): ListenableFuture<LibraryResult<MediaItem>> {
|
||||||
|
if (params != null && params.isRecent) {
|
||||||
|
return Futures.immediateFuture(LibraryResult.ofError(LibraryResult.RESULT_ERROR_NOT_SUPPORTED))
|
||||||
|
}
|
||||||
return Futures.immediateFuture(LibraryResult.ofItem(MediaItemTree.getRootItem(), params))
|
return Futures.immediateFuture(LibraryResult.ofItem(MediaItemTree.getRootItem(), params))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +211,9 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeCastPlayer() {
|
private fun initializeCastPlayer() {
|
||||||
if (UIUtil.isCastApiAvailable(this)) {
|
if (GoogleApiAvailability.getInstance()
|
||||||
|
.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS
|
||||||
|
) {
|
||||||
castPlayer = CastPlayer(CastContext.getSharedInstance(this))
|
castPlayer = CastPlayer(CastContext.getSharedInstance(this))
|
||||||
castPlayer.setSessionAvailabilityListener(this)
|
castPlayer.setSessionAvailabilityListener(this)
|
||||||
}
|
}
|
||||||
@@ -234,11 +241,15 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
|||||||
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
||||||
if (mediaItem == null) return
|
if (mediaItem == null) return
|
||||||
|
|
||||||
if(reason == Player.MEDIA_ITEM_TRANSITION_REASON_SEEK || reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_SEEK || reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
||||||
MediaManager.setLastPlayedTimestamp(mediaItem)
|
MediaManager.setLastPlayedTimestamp(mediaItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onTracksChanged(tracks: Tracks) {
|
||||||
|
ReplayGainUtil.setReplayGain(player, tracks)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
if (!isPlaying) {
|
if (!isPlaying) {
|
||||||
MediaManager.setPlayingPausedTimestamp(
|
MediaManager.setPlayingPausedTimestamp(
|
||||||
@@ -255,7 +266,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
|||||||
) {
|
) {
|
||||||
super.onPositionDiscontinuity(oldPosition, newPosition, reason)
|
super.onPositionDiscontinuity(oldPosition, newPosition, reason)
|
||||||
|
|
||||||
if(reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||||
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||||
MediaManager.scrobble(oldPosition.mediaItem)
|
MediaManager.scrobble(oldPosition.mediaItem)
|
||||||
MediaManager.saveChronology(oldPosition.mediaItem)
|
MediaManager.saveChronology(oldPosition.mediaItem)
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
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 androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
|
import com.cappielloantonio.tempo.databinding.FragmentToolbarBinding;
|
||||||
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
|
import com.google.android.gms.cast.framework.CastButtonFactory;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
public class ToolbarFragment extends Fragment {
|
||||||
|
private static final String TAG = "ToolbarFragment";
|
||||||
|
|
||||||
|
private FragmentToolbarBinding bind;
|
||||||
|
private MainActivity activity;
|
||||||
|
|
||||||
|
public ToolbarFragment() {
|
||||||
|
// Required empty public constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.main_page_menu, menu);
|
||||||
|
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.media_route_menu_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
activity = (MainActivity) getActivity();
|
||||||
|
|
||||||
|
bind = FragmentToolbarBinding.inflate(inflater, container, false);
|
||||||
|
View view = bind.getRoot();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_search) {
|
||||||
|
activity.navController.navigate(R.id.searchFragment);
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_settings) {
|
||||||
|
activity.navController.navigate(R.id.settingsFragment);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.cappielloantonio.tempo.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.framework.CastContext;
|
||||||
|
import com.google.android.gms.common.ConnectionResult;
|
||||||
|
import com.google.android.gms.common.GoogleApiAvailability;
|
||||||
|
|
||||||
|
public class Flavors {
|
||||||
|
public static void initializeCastContext(Context context) {
|
||||||
|
if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS)
|
||||||
|
CastContext.getSharedInstance(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,13 +13,6 @@ allprojects {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
|
||||||
url 'https://jitpack.io'
|
|
||||||
}
|
|
||||||
maven {
|
|
||||||
url 'https://oss.sonatype.org/content/repositories/snapshots'
|
|
||||||
}
|
|
||||||
jcenter()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ org.gradle.jvmargs=-Xmx2048m
|
|||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
# Automatically convert third-party libraries to use AndroidX
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
android.enableJetifier=true
|
android.enableJetifier=false
|
||||||
android.defaults.buildfeatures.buildconfig=true
|
android.defaults.buildfeatures.buildconfig=true
|
||||||
android.nonTransitiveRClass=false
|
android.nonTransitiveRClass=false
|
||||||
android.nonFinalResIds=false
|
android.nonFinalResIds=false
|
||||||
Reference in New Issue
Block a user