Refactor project structure

This commit is contained in:
CappielloAntonio 2021-04-27 10:44:55 +02:00
parent a26c127356
commit 2d886b67ff
46 changed files with 158 additions and 361 deletions

View file

@ -0,0 +1,278 @@
package com.cappielloantonio.play.ui.activity;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.NavigationUI;
import com.cappielloantonio.play.App;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.broadcast.receiver.ConnectivityStatusBroadcastReceiver;
import com.cappielloantonio.play.databinding.ActivityMainBinding;
import com.cappielloantonio.play.service.MusicPlayerRemote;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.ui.activity.base.BaseActivity;
import com.cappielloantonio.play.ui.fragment.PlayerBottomSheetFragment;
import com.cappielloantonio.play.util.PreferenceUtil;
import com.cappielloantonio.play.util.SyncUtil;
import com.cappielloantonio.play.viewmodel.MainViewModel;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import org.jellyfin.apiclient.interaction.EmptyResponse;
import org.jellyfin.apiclient.interaction.Response;
import org.jellyfin.apiclient.model.session.ClientCapabilities;
import org.jellyfin.apiclient.model.system.SystemInfo;
import java.util.Objects;
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";
public ActivityMainBinding bind;
private MainViewModel mainViewModel;
private FragmentManager fragmentManager;
private NavHostFragment navHostFragment;
private BottomNavigationView bottomNavigationView;
public NavController navController;
private BottomSheetBehavior bottomSheetBehavior;
ConnectivityStatusBroadcastReceiver connectivityStatusBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bind = ActivityMainBinding.inflate(getLayoutInflater());
View view = bind.getRoot();
setContentView(view);
mainViewModel = new ViewModelProvider(this).get(MainViewModel.class);
connectivityStatusBroadcastReceiver = new ConnectivityStatusBroadcastReceiver(this);
connectivityStatusReceiverManager(true);
init();
}
@Override
protected void onDestroy() {
super.onDestroy();
connectivityStatusReceiverManager(false);
}
public void init() {
fragmentManager = getSupportFragmentManager();
initBottomSheet();
initNavigation();
if (PreferenceUtil.getInstance(this).getToken() != null) {
checkPreviousSession();
goFromLogin();
} else {
goToLogin();
}
}
private void checkPreviousSession() {
App.getApiClientInstance(getApplicationContext()).ChangeServerLocation(PreferenceUtil.getInstance(this).getServer());
App.getApiClientInstance(getApplicationContext()).SetAuthenticationInfo(PreferenceUtil.getInstance(this).getToken(), PreferenceUtil.getInstance(this).getUser());
App.getApiClientInstance(getApplicationContext()).GetSystemInfoAsync(new Response<SystemInfo>() {
@Override
public void onResponse(SystemInfo result) {
ClientCapabilities clientCapabilities = new ClientCapabilities();
clientCapabilities.setSupportsMediaControl(true);
clientCapabilities.setSupportsPersistentIdentifier(true);
App.getApiClientInstance(getApplicationContext()).ensureWebSocket();
App.getApiClientInstance(getApplicationContext()).ReportCapabilities(clientCapabilities, new EmptyResponse());
}
@Override
public void onError(Exception exception) {
Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
// BOTTOM SHEET/NAVIGATION
private void initBottomSheet() {
bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.player_bottom_sheet));
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback);
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
/*
* All'apertura mostro il bottom sheet solo se in coda c'è qualcosa
*/
isBottomSheetInPeek(mainViewModel.isQueueLoaded());
}
public void isBottomSheetInPeek(Boolean isVisible) {
if (isVisible) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
}
private void initNavigation() {
bottomNavigationView = findViewById(R.id.bottom_navigation);
navHostFragment = (NavHostFragment) fragmentManager.findFragmentById(R.id.nav_host_fragment);
navController = navHostFragment.getNavController();
/*
* In questo modo intercetto il cambio schermata tramite navbar e se il bottom sheet è aperto,
* lo chiudo
*/
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED && (
destination.getId() == R.id.homeFragment ||
destination.getId() == R.id.libraryFragment ||
destination.getId() == R.id.searchFragment ||
destination.getId() == R.id.settingsFragment)
) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
});
NavigationUI.setupWithNavController(bottomNavigationView, navController);
}
public void setBottomNavigationBarVisibility(boolean visibility) {
if (visibility) {
bottomNavigationView.setVisibility(View.VISIBLE);
} else {
bottomNavigationView.setVisibility(View.GONE);
}
}
public void setBottomSheetVisibility(boolean visibility) {
if (visibility) {
findViewById(R.id.player_bottom_sheet).setVisibility(View.VISIBLE);
} else {
findViewById(R.id.player_bottom_sheet).setVisibility(View.GONE);
}
}
private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback =
new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View view, int state) {
switch (state) {
case BottomSheetBehavior.STATE_SETTLING | BottomSheetBehavior.STATE_COLLAPSED:
Log.i(TAG, "onStateChanged: IS_SETTLING | IS_COLLAPSING");
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
if (playerBottomSheetFragment == null) break;
playerBottomSheetFragment.scrollOnTop();
break;
case BottomSheetBehavior.STATE_HIDDEN:
MusicPlayerRemote.quitPlaying();
break;
}
}
@Override
public void onSlide(@NonNull View view, float slideOffset) {
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
if (playerBottomSheetFragment == null) {
return;
}
else {
float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f;
playerBottomSheetFragment.getPlayerHeader().setAlpha(1 - condensedSlideOffset);
playerBottomSheetFragment.getPlayerHeader().setVisibility(condensedSlideOffset > 0.99 ? View.GONE : View.VISIBLE);
}
}
};
/*
* Scroll on top del bottom sheet quando chiudo
* In questo modo non mi ritrovo al posto dell'header una parte centrale del player
*/
public void setBottomSheetMusicInfo(Song song) {
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
if (playerBottomSheetFragment == null) return;
playerBottomSheetFragment.scrollPager(song, 0, false);
}
// NAVIGATION
public void goToLogin() {
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment)
navController.navigate(R.id.action_landingFragment_to_loginFragment);
}
public void goToSync() {
setBottomNavigationBarVisibility(false);
setBottomSheetVisibility(false);
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.loginFragment) {
Bundle bundle = SyncUtil.getSyncBundle(true, true, true, true, true, false);
navController.navigate(R.id.action_loginFragment_to_syncFragment, bundle);
} else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.homeFragment) {
Bundle bundle = SyncUtil.getSyncBundle(true, true, true, true, true, false);
navController.navigate(R.id.action_homeFragment_to_syncFragment, bundle);
} else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.libraryFragment) {
Bundle bundle = SyncUtil.getSyncBundle(false, false, true, false, false, true);
navController.navigate(R.id.action_libraryFragment_to_syncFragment, bundle);
}
}
public void goFromSettingsToSync(Bundle bundle) {
setBottomNavigationBarVisibility(false);
setBottomSheetVisibility(false);
navController.navigate(R.id.action_settingsFragment_to_syncFragment, bundle);
}
public void goToHome() {
bottomNavigationView.setVisibility(View.VISIBLE);
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) {
navController.navigate(R.id.action_landingFragment_to_homeFragment);
} else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.syncFragment) {
navController.navigate(R.id.action_syncFragment_to_homeFragment);
} else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.loginFragment) {
navController.navigate(R.id.action_loginFragment_to_homeFragment);
}
}
public void goFromLogin() {
if (PreferenceUtil.getInstance(getApplicationContext()).getSync()) {
goToHome();
} else {
goToSync();
}
}
@Override
public void onBackPressed() {
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED)
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
else
super.onBackPressed();
}
// CONNECTION
private void connectivityStatusReceiverManager(boolean isActive) {
if (isActive) {
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(connectivityStatusBroadcastReceiver, filter);
} else {
unregisterReceiver(connectivityStatusBroadcastReceiver);
}
}
}

View file

@ -0,0 +1,283 @@
package com.cappielloantonio.play.ui.activity.base;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.service.MusicPlayerRemote;
import com.cappielloantonio.play.interfaces.MusicServiceEventListener;
import com.cappielloantonio.play.service.DownloadTracker;
import com.cappielloantonio.play.service.MusicService;
import com.cappielloantonio.play.service.DownloaderService;
import com.cappielloantonio.play.util.DownloadUtil;
import com.google.android.exoplayer2.offline.DownloadService;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import pub.devrel.easypermissions.AppSettingsDialog;
import pub.devrel.easypermissions.EasyPermissions;
public class BaseActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks, MusicServiceEventListener, DownloadTracker.Listener {
private static final String TAG = "BaseActivity";
public static final int REQUEST_PERM_ACCESS = 1;
private final List<MusicServiceEventListener> mMusicServiceEventListeners = new ArrayList<>();
private MusicPlayerRemote.ServiceToken serviceToken;
private MusicStateReceiver musicStateReceiver;
private DownloadTracker downloadTracker;
private boolean receiverRegistered;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
serviceToken = MusicPlayerRemote.bindToService(this, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected");
BaseActivity.this.onServiceConnected();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
BaseActivity.this.onServiceDisconnected();
}
});
downloadTracker = DownloadUtil.getDownloadTracker(this);
// Start the download service if it should be running but it's not currently.
// Starting the service in the foreground causes notification flicker if there is no scheduled
// action. Starting it in the background throws an exception if the app is in the background too
// (e.g. if device screen is locked).
try {
DownloadService.start(this, DownloaderService.class);
} catch (IllegalStateException e) {
DownloadService.startForeground(this, DownloaderService.class);
}
}
@Override
public void onStart() {
super.onStart();
downloadTracker.addListener(this);
}
@Override
protected void onResume() {
super.onResume();
checkPermissions();
checkBatteryOptimization();
}
@Override
public void onStop() {
downloadTracker.removeListener(this);
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
MusicPlayerRemote.unbindFromService(serviceToken);
if (receiverRegistered) {
unregisterReceiver(musicStateReceiver);
receiverRegistered = false;
}
}
private void checkBatteryOptimization() {
if (detectBatteryOptimization()) {
showBatteryOptimizationDialog();
}
}
private boolean detectBatteryOptimization() {
String packageName = getPackageName();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
return !powerManager.isIgnoringBatteryOptimizations(packageName);
}
private void showBatteryOptimizationDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.battery_optimizations_message)
.setTitle(R.string.battery_optimizations_title)
.setNegativeButton(R.string.ignore, null)
.setPositiveButton(R.string.disable, (dialog, id) -> openPowerSettings())
.show();
}
private void openPowerSettings() {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
startActivity(intent);
}
private void checkPermissions() {
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (!EasyPermissions.hasPermissions(this, permissions)) {
EasyPermissions.requestPermissions(this, getString(R.string.storage_permission_rationale), REQUEST_PERM_ACCESS, permissions);
}
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this).build().show();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
public void addMusicServiceEventListener(final MusicServiceEventListener listener) {
if (listener != null) {
mMusicServiceEventListeners.add(listener);
}
}
public void removeMusicServiceEventListener(final MusicServiceEventListener listener) {
if (listener != null) {
mMusicServiceEventListeners.remove(listener);
}
}
@Override
public void onServiceConnected() {
if (!receiverRegistered) {
musicStateReceiver = new MusicStateReceiver(this);
final IntentFilter filter = new IntentFilter();
filter.addAction(MusicService.STATE_CHANGED);
filter.addAction(MusicService.META_CHANGED);
filter.addAction(MusicService.QUEUE_CHANGED);
registerReceiver(musicStateReceiver, filter);
receiverRegistered = true;
}
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
if (listener != null) {
listener.onServiceConnected();
}
}
}
@Override
public void onServiceDisconnected() {
if (receiverRegistered) {
unregisterReceiver(musicStateReceiver);
receiverRegistered = false;
}
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
if (listener != null) {
listener.onServiceDisconnected();
}
}
}
@Override
public void onQueueChanged() {
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
if (listener != null) {
listener.onQueueChanged();
}
}
}
@Override
public void onPlayMetadataChanged() {
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
if (listener != null) {
listener.onPlayMetadataChanged();
}
}
}
@Override
public void onPlayStateChanged() {
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
if (listener != null) {
listener.onPlayStateChanged();
}
}
}
@Override
public void onRepeatModeChanged() {
for (MusicServiceEventListener listener : mMusicServiceEventListeners) {
if (listener != null) {
listener.onRepeatModeChanged();
}
}
}
@Override
public void onDownloadsChanged() {
// TODO Notificare all'item scaricato che lo stato di download è cambiato
// sampleAdapter.notifyDataSetChanged();
Toast.makeText(this, "Download changed", Toast.LENGTH_SHORT).show();
}
private static final class MusicStateReceiver extends BroadcastReceiver {
private final WeakReference<BaseActivity> reference;
public MusicStateReceiver(final BaseActivity activity) {
reference = new WeakReference<>(activity);
}
@Override
public void onReceive(final Context context, @NonNull final Intent intent) {
final String action = intent.getAction();
BaseActivity activity = reference.get();
if (activity != null && action != null) {
switch (action) {
case MusicService.META_CHANGED:
activity.onPlayMetadataChanged();
break;
case MusicService.QUEUE_CHANGED:
activity.onQueueChanged();
break;
case MusicService.STATE_CHANGED:
activity.onPlayStateChanged();
break;
}
}
}
}
}