package com.simplecity.amp_library.utils; import android.app.Activity; import android.app.Dialog; import android.content.ComponentName; import android.content.ContentProviderOperation; import android.content.Context; import android.content.Intent; import android.content.OperationApplicationException; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.RemoteException; import android.support.annotation.StringRes; import android.support.design.widget.Snackbar; import android.support.v4.content.FileProvider; import android.support.v4.content.IntentCompat; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.NumberPicker; import android.widget.ProgressBar; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.annimon.stream.Collectors; import com.annimon.stream.Stream; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SimpleTarget; import com.simplecity.amp_library.R; import com.simplecity.amp_library.ShuttleApplication; import com.simplecity.amp_library.http.HttpClient; import com.simplecity.amp_library.lastfm.LastFmAlbum; import com.simplecity.amp_library.lastfm.LastFmArtist; import com.simplecity.amp_library.model.AdaptableItem; import com.simplecity.amp_library.model.BlacklistedSong; import com.simplecity.amp_library.model.FileObject; import com.simplecity.amp_library.model.Song; import com.simplecity.amp_library.sql.databases.BlacklistHelper; import com.simplecity.amp_library.sql.databases.WhitelistHelper; import com.simplecity.amp_library.sql.providers.PlayCountTable; import com.simplecity.amp_library.sql.sqlbrite.SqlBriteUtils; import com.simplecity.amp_library.ui.activities.MainActivity; import com.simplecity.amp_library.ui.activities.SettingsActivity; import com.simplecity.amp_library.ui.adapters.BlacklistAdapter; import com.simplecity.amp_library.ui.adapters.ColorAdapter; import com.simplecity.amp_library.ui.adapters.WhitelistAdapter; import com.simplecity.amp_library.ui.fragments.SettingsFragment; import com.simplecity.amp_library.ui.modelviews.BlacklistView; import com.simplecity.amp_library.ui.modelviews.ColorView; import com.simplecity.amp_library.ui.modelviews.EmptyView; import com.simplecity.amp_library.ui.modelviews.WhitelistView; import com.simplecity.amp_library.ui.views.CustomCheckBox; import com.simplecity.amp_library.ui.views.CustomColorPicker; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; import static com.simplecity.amp_library.R.id.album; import static com.simplecity.amp_library.R.id.artist; public class DialogUtils { private static final String TAG = "DialogUtils"; public static MaterialDialog.Builder getBuilder(Context context) { int themeType = ThemeUtils.getInstance().themeType; boolean isDark = themeType == ThemeUtils.ThemeType.TYPE_BLACK || themeType == ThemeUtils.ThemeType.TYPE_DARK || themeType == ThemeUtils.ThemeType.TYPE_SOLID_BLACK || themeType == ThemeUtils.ThemeType.TYPE_SOLID_DARK; int accentColor = ColorUtils.getContrastAwareColorAccent(context); return new MaterialDialog.Builder(context) .titleColorRes(isDark ? R.color.primary_text_dark : R.color.primary_text_light) .contentColorRes(isDark ? R.color.primary_text_dark : R.color.primary_text_light) .dividerColor(accentColor) .backgroundColorRes(isDark ? R.color.bg_dark : R.color.bg_light) .positiveColor(accentColor) .neutralColor(accentColor) .negativeColor(accentColor) .widgetColor(accentColor) .buttonRippleColor(accentColor); } public static void showChangelog(Context context) { View customView = LayoutInflater.from(context).inflate(R.layout.dialog_changelog, null); WebView webView = (WebView) customView.findViewById(R.id.webView); int themeType = ThemeUtils.getThemeType(context); webView.setBackgroundColor(context.getResources().getColor(android.R.color.transparent)); CustomCheckBox checkBox = (CustomCheckBox) customView.findViewById(R.id.checkbox); checkBox.setChecked(SettingsManager.getInstance().getShowChangelogOnLaunch()); checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> SettingsManager.getInstance().setShowChangelogOnLaunch(isChecked)); ProgressBar progressBar = (ProgressBar) customView.findViewById(R.id.progress); webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); ViewUtils.fadeOut(progressBar, () -> ViewUtils.fadeIn(webView, null)); } }); if (themeType == ThemeUtils.ThemeType.TYPE_LIGHT || themeType == ThemeUtils.ThemeType.TYPE_SOLID_LIGHT) { webView.loadUrl("file:///android_asset/web/info.html"); } else { webView.loadUrl("file:///android_asset/web/info_dark.html"); } getBuilder(context) .title(R.string.pref_title_about) .customView(customView, false) .negativeText(R.string.close) .show(); AnalyticsManager.logChangelogViewed(); } public interface ColorSelectionListener { void colorSelected(int color); } public static void showColorPickerDialog(SettingsFragment fragment, int selectedColor, ColorSelectionListener listener) { showColorPickerDialog(fragment, selectedColor, ColorPalette.getPrimaryColors(), ColorPalette.getPrimaryColorsSub(), listener); } public static void showColorPickerDialog(SettingsFragment fragment, int selectedColor, int[] mainColors, int[][] subColors, ColorSelectionListener listener) { View customView = LayoutInflater.from(fragment.getActivity()).inflate(R.layout.dialog_color_picker, null); RecyclerView recyclerView = (RecyclerView) customView.findViewById(R.id.recyclerView); GridLayoutManager gridLayoutManager = new GridLayoutManager(fragment.getActivity(), 5); recyclerView.setLayoutManager(gridLayoutManager); ThemeUtils.themeRecyclerView(recyclerView); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { ThemeUtils.themeRecyclerView(recyclerView); super.onScrollStateChanged(recyclerView, newState); } }); ColorAdapter colorAdapter = new ColorAdapter(); List<AdaptableItem> colorViews = new ArrayList<>(); for (int i = 0, length = mainColors.length; i < length; i++) { ColorView colorView = new ColorView(mainColors[i]); boolean selected = false; //If the sub colors array contains our selected color, then we set this colorView to selected. for (int j = 0, jLength = subColors[i].length; j < jLength; j++) { if (subColors[i][j] == selectedColor) { selected = true; } } colorView.selected = selected; colorViews.add(colorView); } colorAdapter.setItems(colorViews); recyclerView.setAdapter(colorAdapter); colorAdapter.setColorListener((position, color, isSubColor) -> { if (isSubColor) { colorAdapter.setSelectedPosition(position); } else { List<AdaptableItem> subColorViews = new ArrayList<>(); for (int i = 0, length = subColors[position].length; i < length; i++) { ColorView colorView = new ColorView(subColors[position][i]); colorView.selected = colorView.color == selectedColor; subColorViews.add(colorView); colorAdapter.isSubColor = true; } colorAdapter.setItems(subColorViews); } }); int neutralTextResId; TextView textView = (TextView) customView.findViewById(R.id.text1); if (ShuttleUtils.isUpgraded()) { textView.setVisibility(View.GONE); neutralTextResId = R.string.dialog_custom; } else { textView.setVisibility(View.VISIBLE); if (ShuttleUtils.isAmazonBuild()) { neutralTextResId = R.string.get_pro_button_amazon; } else { neutralTextResId = R.string.btn_upgrade; } } getBuilder(fragment.getActivity()) .title(fragment.getActivity().getString(R.string.color_pick)) .negativeText(R.string.cancel) .onNegative((dialog, which) -> dialog.dismiss()) .positiveText(R.string.button_done) .onPositive((dialog, which) -> { int color = selectedColor; for (AdaptableItem item : colorAdapter.items) { if (((ColorView) item).selected) { color = ((ColorView) item).color; break; } } listener.colorSelected(color); dialog.dismiss(); }) .neutralText(neutralTextResId) .autoDismiss(false) .onNeutral((dialog, which) -> { if (ShuttleUtils.isUpgraded()) { showCustomColorPickerDialog(fragment.getActivity(), selectedColor, listener); dialog.dismiss(); } else { showUpgradeDialog(fragment.getActivity(), (upgradeDialog, which1) -> { if (ShuttleUtils.isAmazonBuild()) { ShuttleUtils.openShuttleLink(fragment.getActivity(), "com.simplecity.amp_pro"); } else { AnalyticsManager.logUpgrade(AnalyticsManager.UpgradeType.COLORS); ((SettingsActivity) fragment.getActivity()).purchasePremiumUpgrade(); } }); } }) .customView(customView, false) .show(); } public static void showCustomColorPickerDialog(Context context, int selectedColor, ColorSelectionListener listener) { CustomColorPicker customColorPicker = new CustomColorPicker(context, selectedColor); getBuilder(context) .title(context.getString(R.string.color_pick)) .negativeText(R.string.cancel) .positiveText(R.string.button_done) .onPositive((d, which) -> listener.colorSelected(customColorPicker.color)) .customView(customColorPicker, false) .show(); } public static class DeleteDialogBuilder { private Context context; private @StringRes int deleteSingleMessageId; private @StringRes int deleteMultipleMessageId; private List<String> itemNames; private Observable<List<Song>> songsObservable; public DeleteDialogBuilder context(Context context) { this.context = context; return this; } public DeleteDialogBuilder singleMessageId(@StringRes int deleteSingleMessageId) { this.deleteSingleMessageId = deleteSingleMessageId; return this; } public DeleteDialogBuilder multipleMessage(@StringRes int deleteMultipleMessageId) { this.deleteMultipleMessageId = deleteMultipleMessageId; return this; } public DeleteDialogBuilder itemNames(List<String> itemNames) { this.itemNames = itemNames; return this; } public DeleteDialogBuilder songsToDelete(Observable<List<Song>> songsObservable) { this.songsObservable = songsObservable; return this; } void deleteSongs() { songsObservable .map(lists -> Stream.of(lists) .flatMap(Stream::of) .filter(Song::delete) .collect(Collectors.toList())) .doOnNext(songs -> { //Current play queue MusicUtils.removeFromQueue(songs, true); //Play Count Table ArrayList<ContentProviderOperation> operations = Stream.of(songs).map(song -> ContentProviderOperation .newDelete(PlayCountTable.URI) .withSelection(PlayCountTable.COLUMN_ID + "=" + song.id, null) .build()) .collect(Collectors.toCollection(ArrayList<ContentProviderOperation>::new)); try { context.getContentResolver().applyBatch(PlayCountTable.AUTHORITY, operations); } catch (RemoteException | OperationApplicationException e) { e.printStackTrace(); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(deletedSongs -> { if (deletedSongs.size() > 0) { CustomMediaScanner.scanFiles(Stream.of(deletedSongs) .map(song -> song.path) .collect(Collectors.toList()), null); Toast.makeText(context, String.format(context.getString(R.string.delete_songs_success_toast), deletedSongs.size()), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, R.string.delete_songs_failure_toast, Toast.LENGTH_SHORT).show(); } }); } public MaterialDialog build() { String stringToFormat = context.getString(deleteSingleMessageId); String names; if (itemNames.size() > 1) { stringToFormat = context.getString(deleteMultipleMessageId); names = Stream.of(itemNames) .map(itemName -> "\n\u2022 " + itemName) .collect(Collectors.joining()) + "\n"; } else { names = itemNames.get(0); } String message = String.format(stringToFormat, names); Drawable drawable = DrawableUtils.getBaseDrawable(context, R.drawable.ic_dialog_alert); return getBuilder(context) .icon(drawable) .title(R.string.delete_item) .content(message) .positiveText(R.string.button_ok) .onPositive((materialDialog, dialogAction) -> deleteSongs()) .negativeText(R.string.cancel) .build(); } } public static void createRestartDialog(final Context context) { MaterialDialog.Builder builder = getBuilder(context) .title(R.string.restart_tite) .content(R.string.restart_message) .positiveText(R.string.restart_button) .onPositive((materialDialog, dialogAction) -> { Intent intent = new Intent(context, MainActivity.class); ComponentName componentNAme = intent.getComponent(); Intent mainIntent = IntentCompat.makeRestartActivityTask(componentNAme); context.startActivity(mainIntent); }); if (!((Activity) context).isFinishing()) { builder.show(); } } public static void showShareDialog(final Context context, final Song song) { if (song == null) { Toast.makeText(context, context.getString(R.string.share_failed), Toast.LENGTH_LONG).show(); return; } LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.dialog_listview, null); final ListView listView = (ListView) view.findViewById(android.R.id.list); ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(context, R.layout.dialog_list_item); arrayAdapter.add(context.getString(R.string.share_option_song_info)); arrayAdapter.add(context.getString(R.string.share_option_audio_file)); listView.setAdapter(arrayAdapter); MaterialDialog.Builder builder = getBuilder(context) .title(context.getString(R.string.share_dialog_title)) .customView(view, false); final Dialog dialog = builder.show(); builder.negativeText(R.string.close); listView.setOnItemClickListener((adapterView, view1, i, l) -> { switch (i) { case 0: // Use the compress method on the Bitmap object to write image to the OutputStream Glide.with(context) .load(song) .asBitmap() .diskCacheStrategy(DiskCacheStrategy.SOURCE) .into(new SimpleTarget<Bitmap>() { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { Intent sendIntent = new Intent(); sendIntent.setType("text/plain"); FileOutputStream fileOutputStream = null; try { File file = new File(context.getFilesDir() + "/share_image.jpg"); fileOutputStream = new FileOutputStream(file); if (resource != null) { resource.compress(Bitmap.CompressFormat.JPEG, 80, fileOutputStream); sendIntent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file)); sendIntent.setType("image/jpeg"); } } catch (FileNotFoundException ignored) { } finally { try { if (fileOutputStream != null) { fileOutputStream.close(); } } catch (IOException ignored) { } } sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, "#NowPlaying " + song.artistName + " - " + song.name + "\n\n" + "#Shuttle"); context.startActivity(Intent.createChooser(sendIntent, "Share current song via: ")); dialog.dismiss(); } }); break; case 1: song.share(context); dialog.dismiss(); break; } }); } /** * Displays the popup dialog recommending the user try the paid version */ public static void showUpgradeNagDialog(final Context context, MaterialDialog.SingleButtonCallback listener) { //If we're in the free version, the app has been launched more than 15 times, //The message hasn't been read before, display the 'upgrade to pro' dialog. if (!ShuttleUtils.isUpgraded() && SettingsManager.getInstance().getLaunchCount() > 15 && !SettingsManager.getInstance().getNagMessageRead()) { MaterialDialog.Builder builder = getBuilder(context) .title(context.getResources().getString(R.string.get_pro_title)) .content(context.getResources().getString(R.string.get_pro_message)) .positiveText(R.string.btn_upgrade) .onPositive(listener) .negativeText(R.string.get_pro_button_no); builder.show(); SettingsManager.getInstance().setNagMessageRead(); AnalyticsManager.logUpgrade(AnalyticsManager.UpgradeType.NAG); } } /** * Displayed when the user chooses to upgrade */ public static void showUpgradeDialog(final Context context, MaterialDialog.SingleButtonCallback listener) { getBuilder(context) .title(context.getResources().getString(R.string.get_pro_title)) .content(context.getResources().getString(R.string.upgrade_dialog_message)) .positiveText(R.string.btn_upgrade) .onPositive(listener) .negativeText(R.string.get_pro_button_no) .show(); AnalyticsManager.logUpgrade(AnalyticsManager.UpgradeType.UPGRADE); } /** * Displays the popup dialog recommending the user try the paid version */ public static void showUpgradeThankyouDialog(final Context context) { getBuilder(context) .title(context.getResources().getString(R.string.upgraded_title)) .content(context.getResources().getString(R.string.upgraded_message)) .positiveText(R.string.restart_button) .onPositive((materialDialog, dialogAction) -> { Intent intent = new Intent(context, MainActivity.class); ComponentName componentNAme = intent.getComponent(); Intent mainIntent = IntentCompat.makeRestartActivityTask(componentNAme); context.startActivity(mainIntent); }) .show(); } public @interface BioType { int ARTIST = 0; int ALBUM = 1; } public static void showArtistBiographyDialog(final Context context, String artistName) { showBiographyDialog(context, BioType.ARTIST, artistName, null); } public static void showAlbumBiographyDialog(final Context context, String artistName, String albumName) { showBiographyDialog(context, BioType.ALBUM, artistName, albumName); } public static void showBiographyDialog(final Context context, @BioType int type, String artistName, String albumName) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View customView = inflater.inflate(R.layout.dialog_biography, null, false); final ProgressBar progressBar = (ProgressBar) customView.findViewById(R.id.progress); final TextView message = (TextView) customView.findViewById(R.id.message); final ScrollView scrollView = (ScrollView) customView.findViewById(R.id.scrollView); ThemeUtils.themeScrollView(scrollView); Callback<LastFmArtist> artistCallback = new Callback<LastFmArtist>() { @Override public void onResponse(Call<LastFmArtist> call, Response<LastFmArtist> response) { progressBar.setVisibility(View.GONE); if (response != null && response.isSuccessful()) { if (response.body() != null && response.body().artist != null && response.body().artist.bio != null) { message.setText(Html.fromHtml(response.body().artist.bio.summary)); } else { message.setText(R.string.no_artist_info); } } } @Override public void onFailure(Call<LastFmArtist> call, Throwable t) { progressBar.setVisibility(View.GONE); switch (type) { case BioType.ARTIST: message.setText(R.string.no_artist_info); break; case BioType.ALBUM: message.setText(R.string.no_album_info); break; } } }; Callback<LastFmAlbum> albumCallback = new Callback<LastFmAlbum>() { @Override public void onResponse(Call<LastFmAlbum> call, Response<LastFmAlbum> response) { progressBar.setVisibility(View.GONE); if (response != null && response.isSuccessful()) { if (response.body() != null && response.body().album != null && response.body().album.wiki != null) { message.setText(Html.fromHtml(response.body().album.wiki.summary)); } else { message.setText(R.string.no_album_info); } } } @Override public void onFailure(Call<LastFmAlbum> call, Throwable t) { progressBar.setVisibility(View.GONE); switch (type) { case BioType.ARTIST: message.setText(R.string.no_artist_info); break; case BioType.ALBUM: message.setText(R.string.no_album_info); break; } } }; switch (type) { case BioType.ARTIST: HttpClient.getInstance().lastFmService.getLastFmArtistResult(artistName).enqueue(artistCallback); break; case BioType.ALBUM: HttpClient.getInstance().lastFmService.getLastFmAlbumResult(artistName, albumName).enqueue(albumCallback); break; } MaterialDialog.Builder builder = getBuilder(context) .title(R.string.info) .customView(customView, false) .negativeText(R.string.close); Dialog dialog = builder.show(); dialog.setOnDismissListener(dialog1 -> { }); } public static void showBlacklistDialog(final Context context) { View view = LayoutInflater.from(context).inflate(R.layout.dialog_blacklist, null); final MaterialDialog.Builder builder = getBuilder(context) .title(R.string.blacklist_title) .customView(view, false) .positiveText(R.string.close) .negativeText(R.string.pref_title_clear_blacklist) .onNegative((materialDialog, dialogAction) -> { BlacklistHelper.deleteAllSongs(); Toast.makeText(context, R.string.blacklist_deleted, Toast.LENGTH_SHORT).show(); }); final Dialog dialog = builder.build(); RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(context)); final BlacklistAdapter blacklistAdapter = new BlacklistAdapter(); blacklistAdapter.setBlackListListener((v, position, song) -> { BlacklistHelper.deleteSong(song.id); if (blacklistAdapter.items.size() == 0) { dialog.dismiss(); } }); recyclerView.setAdapter(blacklistAdapter); Observable<List<Song>> songsObservable = SqlBriteUtils.createContinuousQuery(ShuttleApplication.getInstance(), Song::new, Song.getQuery()).first(); Observable<List<BlacklistedSong>> blacklistObservable = BlacklistHelper.getBlacklistSongsObservable(); Subscription subscription = Observable.combineLatest(songsObservable, blacklistObservable, (songs, blacklistedSongs) -> Stream.of(songs) .filter(song -> Stream.of(blacklistedSongs) .anyMatch(blacklistedSong -> blacklistedSong.songId == song.id)) .sorted((a, b) -> ComparisonUtils.compare(a.albumArtistName, b.albumArtistName)) .sorted((a, b) -> ComparisonUtils.compareInt(b.year, a.year)) .sorted((a, b) -> ComparisonUtils.compareInt(a.track, b.track)) .sorted((a, b) -> ComparisonUtils.compareInt(a.discNumber, b.discNumber)) .sorted((a, b) -> ComparisonUtils.compare(a.albumName, b.albumName)) .map(song -> (AdaptableItem) new BlacklistView(song)) .collect(Collectors.toList())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(blacklistViews -> { if (blacklistViews.size() == 0) { blacklistAdapter.addItem(0, new EmptyView(R.string.blacklist_empty)); } else { blacklistAdapter.setItems(blacklistViews); } }); dialog.setOnDismissListener(dialogInterface -> subscription.unsubscribe()); dialog.show(); } public static void showWhitelistDialog(final Context context) { View view = LayoutInflater.from(context).inflate(R.layout.dialog_blacklist, null); final MaterialDialog.Builder builder = getBuilder(context) .title(R.string.whitelist_title) .customView(view, false) .positiveText(R.string.close) .negativeText(R.string.pref_title_clear_whitelist) .onNegative((materialDialog, dialogAction) -> { WhitelistHelper.deleteAllFolders(); Toast.makeText(context, R.string.whitelist_deleted, Toast.LENGTH_SHORT).show(); }); final Dialog dialog = builder.build(); RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(context)); final WhitelistAdapter whitelistadapter = new WhitelistAdapter(); whitelistadapter.setWhitelistListener((v, position, songWhitelist) -> { WhitelistHelper.deleteFolder(songWhitelist); whitelistadapter.removeItem(position); if (whitelistadapter.items.size() == 0) { dialog.dismiss(); } }); recyclerView.setAdapter(whitelistadapter); Subscription subscription = WhitelistHelper.getWhitelistFolders() .map(whitelistFolders -> Stream.of(whitelistFolders) .map(folder -> (AdaptableItem) new WhitelistView(folder)) .collect(Collectors.toList())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(whitelistViews -> { if (whitelistViews.size() == 0) { whitelistadapter.addItem(0, new EmptyView(R.string.whitelist_empty)); } else { whitelistadapter.setItems(whitelistViews); } }); dialog.setOnDismissListener(dialogInterface -> subscription.unsubscribe()); dialog.show(); } public static void showSongInfoDialog(Context context, Song song) { if (song == null) { return; } View view = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.dialog_song_info, null); View titleView = view.findViewById(R.id.title); TextView titleKey = (TextView) titleView.findViewById(R.id.key); titleKey.setText(R.string.song_title); TextView titleValue = (TextView) titleView.findViewById(R.id.value); titleValue.setText(song.name); View trackNumberView = view.findViewById(R.id.track_number); TextView trackNumberKey = (TextView) trackNumberView.findViewById(R.id.key); trackNumberKey.setText(R.string.track_number); TextView trackNumberValue = (TextView) trackNumberView.findViewById(R.id.value); trackNumberValue.setText(String.valueOf(song.getTrackNumberLabel())); View artistView = view.findViewById(artist); TextView artistKey = (TextView) artistView.findViewById(R.id.key); artistKey.setText(R.string.artist_title); TextView artistValue = (TextView) artistView.findViewById(R.id.value); artistValue.setText(song.artistName); View albumView = view.findViewById(album); TextView albumKey = (TextView) albumView.findViewById(R.id.key); albumKey.setText(R.string.album_title); TextView albumValue = (TextView) albumView.findViewById(R.id.value); albumValue.setText(song.albumName); View genreView = view.findViewById(R.id.genre); TextView genreKey = (TextView) genreView.findViewById(R.id.key); genreKey.setText(R.string.genre_title); TextView genreValue = (TextView) genreView.findViewById(R.id.value); Observable.fromCallable(song::getGenre) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(genre -> genreValue.setText(genre == null ? null : genre.name)); View albumArtistView = view.findViewById(R.id.album_artist); TextView albumArtistKey = (TextView) albumArtistView.findViewById(R.id.key); albumArtistKey.setText(R.string.album_artist_title); TextView albumArtistValue = (TextView) albumArtistView.findViewById(R.id.value); albumArtistValue.setText(song.albumArtistName); View durationView = view.findViewById(R.id.duration); TextView durationKey = (TextView) durationView.findViewById(R.id.key); durationKey.setText(R.string.sort_song_duration); TextView durationValue = (TextView) durationView.findViewById(R.id.value); durationValue.setText(song.getDurationLabel()); View pathView = view.findViewById(R.id.path); TextView pathKey = (TextView) pathView.findViewById(R.id.key); pathKey.setText(R.string.song_info_path); TextView pathValue = (TextView) pathView.findViewById(R.id.value); pathValue.setText(song.path); View discNumberView = view.findViewById(R.id.disc_number); TextView discNumberKey = (TextView) discNumberView.findViewById(R.id.key); discNumberKey.setText(R.string.disc_number); TextView discNumberValue = (TextView) discNumberView.findViewById(R.id.value); discNumberValue.setText(String.valueOf(song.getDiscNumberLabel())); View fileSizeView = view.findViewById(R.id.file_size); TextView fileSizeKey = (TextView) fileSizeView.findViewById(R.id.key); fileSizeKey.setText(R.string.song_info_file_size); TextView fileSizeValue = (TextView) fileSizeView.findViewById(R.id.value); fileSizeValue.setText(song.getFileSizeLabel()); View formatView = view.findViewById(R.id.format); TextView formatKey = (TextView) formatView.findViewById(R.id.key); formatKey.setText(R.string.song_info_format); TextView formatValue = (TextView) formatView.findViewById(R.id.value); formatValue.setText(song.getFormatLabel()); View bitrateView = view.findViewById(R.id.bitrate); TextView bitrateKey = (TextView) bitrateView.findViewById(R.id.key); bitrateKey.setText(R.string.song_info_bitrate); TextView bitrateValue = (TextView) bitrateView.findViewById(R.id.value); bitrateValue.setText(song.getBitrateLabel()); View samplingRateView = view.findViewById(R.id.sample_rate); TextView samplingRateKey = (TextView) samplingRateView.findViewById(R.id.key); samplingRateKey.setText(R.string.song_info_sample_Rate); TextView samplingRateValue = (TextView) samplingRateView.findViewById(R.id.value); samplingRateValue.setText(song.getSampleRateLabel()); View playCountView = view.findViewById(R.id.play_count); TextView playCountKey = (TextView) playCountView.findViewById(R.id.key); playCountKey.setText(R.string.song_info_play_count); TextView playCountValue = (TextView) playCountView.findViewById(R.id.value); Observable.fromCallable(() -> song.getPlayCount(context)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(playCount -> playCountValue.setText(String.valueOf(playCount))); getBuilder(context) .title(context.getString(R.string.dialog_song_info_title)) .customView(view, false) .negativeText(R.string.close) .show(); } public static void showFileInfoDialog(Context context, FileObject fileObject) { if (fileObject == null) { return; } View view = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.dialog_song_info, null); View titleView = view.findViewById(R.id.title); TextView titleKey = (TextView) titleView.findViewById(R.id.key); titleKey.setText(R.string.song_title); TextView titleValue = (TextView) titleView.findViewById(R.id.value); titleValue.setText(fileObject.tagInfo.trackName); View trackNumberView = view.findViewById(R.id.track_number); TextView trackNumberKey = (TextView) trackNumberView.findViewById(R.id.key); trackNumberKey.setText(R.string.track_number); TextView trackNumberValue = (TextView) trackNumberView.findViewById(R.id.value); if (fileObject.tagInfo.trackTotal != 0) { trackNumberValue.setText(String.format(context.getString(R.string.track_count), String.valueOf(fileObject.tagInfo.trackNumber), String.valueOf(fileObject.tagInfo.trackTotal))); } else { trackNumberValue.setText(String.valueOf(fileObject.tagInfo.trackNumber)); } View artistView = view.findViewById(artist); TextView artistKey = (TextView) artistView.findViewById(R.id.key); artistKey.setText(R.string.artist_title); TextView artistValue = (TextView) artistView.findViewById(R.id.value); artistValue.setText(fileObject.tagInfo.artistName); View albumView = view.findViewById(album); TextView albumKey = (TextView) albumView.findViewById(R.id.key); albumKey.setText(R.string.album_title); TextView albumValue = (TextView) albumView.findViewById(R.id.value); albumValue.setText(fileObject.tagInfo.albumName); View genreView = view.findViewById(R.id.genre); TextView genreKey = (TextView) genreView.findViewById(R.id.key); genreKey.setText(R.string.genre_title); TextView genreValue = (TextView) genreView.findViewById(R.id.value); genreValue.setText(fileObject.tagInfo.genre); View albumArtistView = view.findViewById(R.id.album_artist); TextView albumArtistKey = (TextView) albumArtistView.findViewById(R.id.key); albumArtistKey.setText(R.string.album_artist_title); TextView albumArtistValue = (TextView) albumArtistView.findViewById(R.id.value); albumArtistValue.setText(fileObject.tagInfo.albumArtistName); View durationView = view.findViewById(R.id.duration); TextView durationKey = (TextView) durationView.findViewById(R.id.key); durationKey.setText(R.string.sort_song_duration); TextView durationValue = (TextView) durationView.findViewById(R.id.value); durationValue.setText(fileObject.getTimeString()); View pathView = view.findViewById(R.id.path); TextView pathKey = (TextView) pathView.findViewById(R.id.key); pathKey.setText(R.string.song_info_path); TextView pathValue = (TextView) pathView.findViewById(R.id.value); pathValue.setText(fileObject.path + "/" + fileObject.name + "." + fileObject.extension); View discNumberView = view.findViewById(R.id.disc_number); TextView discNumberKey = (TextView) discNumberView.findViewById(R.id.key); discNumberKey.setText(R.string.disc_number); TextView discNumberValue = (TextView) discNumberView.findViewById(R.id.value); if (fileObject.tagInfo.discTotal != 0) { discNumberValue.setText(String.format(context.getString(R.string.track_count), String.valueOf(fileObject.tagInfo.discNumber), String.valueOf(fileObject.tagInfo.discTotal))); } else { discNumberValue.setText(String.valueOf(fileObject.tagInfo.discNumber)); } View fileSizeView = view.findViewById(R.id.file_size); TextView fileSizeKey = (TextView) fileSizeView.findViewById(R.id.key); fileSizeKey.setText(R.string.song_info_file_size); TextView fileSizeValue = (TextView) fileSizeView.findViewById(R.id.value); fileSizeValue.setText(FileHelper.getHumanReadableSize(fileObject.size)); View formatView = view.findViewById(R.id.format); TextView formatKey = (TextView) formatView.findViewById(R.id.key); formatKey.setText(R.string.song_info_format); TextView formatValue = (TextView) formatView.findViewById(R.id.value); formatValue.setText(fileObject.tagInfo.format); View bitrateView = view.findViewById(R.id.bitrate); TextView bitrateKey = (TextView) bitrateView.findViewById(R.id.key); bitrateKey.setText(R.string.song_info_bitrate); TextView bitrateValue = (TextView) bitrateView.findViewById(R.id.value); bitrateValue.setText(fileObject.tagInfo.bitrate + ShuttleApplication.getInstance().getString(R.string.song_info_bitrate_suffix)); View samplingRateView = view.findViewById(R.id.sample_rate); TextView samplingRateKey = (TextView) samplingRateView.findViewById(R.id.key); samplingRateKey.setText(R.string.song_info_sample_Rate); TextView samplingRateValue = (TextView) samplingRateView.findViewById(R.id.value); samplingRateValue.setText(fileObject.tagInfo.sampleRate / 1000 + ShuttleApplication.getInstance().getString(R.string.song_info_sample_rate_suffix)); View playCountView = view.findViewById(R.id.play_count); playCountView.setVisibility(View.GONE); getBuilder(context) .title(context.getString(R.string.dialog_song_info_title)) .customView(view, false) .negativeText(R.string.close) .show(); } public static void showDownloadWarningDialog(Context context, MaterialDialog.SingleButtonCallback listener) { getBuilder(context) .title(R.string.pref_title_download_artwork) .content(R.string.pref_warning_download_artwork) .positiveText(R.string.download) .onPositive(listener) .negativeText(R.string.cancel) .show(); } public static void showWeekSelectorDialog(final Context context, final MaterialDialog.SingleButtonCallback listener) { View view = LayoutInflater.from(context).inflate(R.layout.weekpicker, null); final NumberPicker numberPicker; numberPicker = (NumberPicker) view.findViewById(R.id.weeks); numberPicker.setMaxValue(12); numberPicker.setMinValue(1); numberPicker.setValue(MusicUtils.getIntPref(context, "numweeks", 2)); getBuilder(context) .title(R.string.week_selector) .customView(view, false) .negativeText(R.string.cancel) .positiveText(R.string.picker_set) .onPositive((materialDialog, dialogAction) -> { int numweeks; numweeks = numberPicker.getValue(); MusicUtils.setIntPref(context, "numweeks", numweeks); if (listener != null) { listener.onClick(materialDialog, dialogAction); } }) .show(); } public static void showRateSnackbar(final Activity activity, final View view) { //If the user hasn't pressed 'rate' in the past if (!SettingsManager.getInstance().getHasRated()) { //If this is the tenth launch, or a multiple of 50 if (SettingsManager.getInstance().getLaunchCount() == 10 || (SettingsManager.getInstance().getLaunchCount() != 0 && SettingsManager.getInstance().getLaunchCount() % 50 == 0)) { Snackbar snackbar = Snackbar.make(view, R.string.snackbar_rate_text, Snackbar.LENGTH_INDEFINITE) .setDuration(15000) .setAction(R.string.snackbar_rate_action, v -> { final String appPackageName = ShuttleApplication.getInstance().getPackageName(); ShuttleUtils.openShuttleLink(activity, appPackageName); SettingsManager.getInstance().setHasRated(); }) .setActionTextColor(ColorUtils.getAccentColor()); snackbar.show(); TextView snackbarText = (TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text); if (snackbarText != null) { snackbarText.setTextColor(Color.WHITE); } } } } }