package tv.emby.embyatv.browsing; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.HeaderItem; import android.support.v17.leanback.widget.ListRow; import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; import android.app.AlertDialog; import android.widget.Toast; import java.io.IOException; import mediabrowser.apiinteraction.EmptyResponse; import mediabrowser.apiinteraction.Response; import mediabrowser.model.entities.SortOrder; import mediabrowser.model.livetv.RecommendedProgramQuery; import mediabrowser.model.livetv.RecordingQuery; import mediabrowser.model.querying.ItemFields; import mediabrowser.model.querying.ItemFilter; import mediabrowser.model.querying.ItemSortBy; import mediabrowser.model.querying.ItemsResult; import mediabrowser.model.querying.NextUpQuery; import tv.emby.embyatv.R; import tv.emby.embyatv.TvApp; import tv.emby.embyatv.base.CustomMessage; import tv.emby.embyatv.base.IMessageListener; import tv.emby.embyatv.integration.RecommendationManager; import tv.emby.embyatv.itemhandling.ItemRowAdapter; import tv.emby.embyatv.model.ChangeTriggerType; import tv.emby.embyatv.playback.AudioEventListener; import tv.emby.embyatv.playback.MediaManager; import tv.emby.embyatv.presentation.GridButtonPresenter; import tv.emby.embyatv.presentation.ThemeManager; import tv.emby.embyatv.querying.QueryType; import tv.emby.embyatv.querying.StdItemQuery; import tv.emby.embyatv.querying.ViewQuery; import tv.emby.embyatv.settings.SettingsActivity; import tv.emby.embyatv.startup.LogonCredentials; import tv.emby.embyatv.startup.SelectUserActivity; import tv.emby.embyatv.ui.GridButton; import tv.emby.embyatv.util.Utils; import tv.emby.embyatv.validation.UnlockActivity; /** * Created by Eric on 12/4/2014. */ public class HomeFragment extends StdBrowseFragment { private static final int LOGOUT = 0; private static final int SETTINGS = 1; private static final int REPORT = 2; private static final int UNLOCK = 3; private static final int LOGOUT_CONNECT = 4; private ArrayObjectAdapter toolsRow; private GridButton unlockButton; private GridButton sendLogsButton; private GridButton premiereButton; @Override public void onActivityCreated(Bundle savedInstanceState) { MainTitle = this.getString(R.string.home_title); //Validate the app TvApp.getApplication().validate(); super.onActivityCreated(savedInstanceState); //Save last login so we can get back proper context on entry try { Utils.SaveLoginCredentials(new LogonCredentials(TvApp.getApplication().getApiClient().getServerInfo(), TvApp.getApplication().getCurrentUser()), "tv.emby.lastlogin.json"); } catch (IOException e) { TvApp.getApplication().getLogger().ErrorException("Unable to save login creds", e); } //Init recommendations RecommendationManager.init(); //Get auto bitrate TvApp.getApplication().determineAutoBitrate(); //First time audio message if (!mApplication.getSystemPrefs().getBoolean("syspref_audio_warned", false)) { mApplication.getSystemPrefs().edit().putBoolean("syspref_audio_warned",true).apply(); new AlertDialog.Builder(mActivity) .setTitle(mApplication.getString(R.string.lbl_audio_capabilitites)) .setMessage(mApplication.getString(R.string.msg_audio_warning)) .setPositiveButton(mApplication.getString(R.string.btn_got_it), null) .setNegativeButton(mApplication.getString(R.string.btn_set_compatible_audio), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mApplication.getPrefs().edit().putString("pref_audio_option", "1").apply(); } }) .setCancelable(false) .show(); } ThemeManager.showWelcomeMessage(); //BETA message // new Handler().postDelayed(new Runnable() { // @Override // public void run() { // mActivity.showMessage("Thank You for Testing", "Thank you for helping to test this app. Please check out the new music functionality and LEAVE FEEDBACK in the testing forum at emby.media/community.",10000); // // } // }, 2000); //Subscribe to Audio messages MediaManager.addAudioEventListener(audioEventListener); //Setup activity messages mActivity.registerMessageListener(new IMessageListener() { @Override public void onMessageReceived(CustomMessage message) { switch (message) { case RefreshRows: if (hasResumeRow()) { refreshRows(); } else { addContinueWatching(); } break; } } }); } @Override public void onResume() { super.onResume(); //if we were locked before and have just unlocked, remove the button if (unlockButton != null && (TvApp.getApplication().isRegistered() || TvApp.getApplication().isPaid())) { toolsRow.remove(unlockButton); if (!TvApp.getApplication().isRegistered()) { premiereButton = new GridButton(UNLOCK, mApplication.getString(R.string.btn_emby_premiere), R.drawable.embyicon); toolsRow.add(premiereButton); } } else { if (premiereButton != null && TvApp.getApplication().isRegistered()) { toolsRow.remove(premiereButton); } } addLogsButton(); //make sure rows have had a chance to be created new Handler().postDelayed(new Runnable() { @Override public void run() { addNowPlaying(); //check for resume row and add if not there if (!hasResumeRow()) addContinueWatching(); } }, 750); } @Override public void onDestroy() { super.onDestroy(); MediaManager.removeAudioEventListener(audioEventListener); } @Override protected void setupQueries(final IRowLoader rowLoader) { //Peek at the first library item to determine our order TvApp.getApplication().getApiClient().GetUserViews(TvApp.getApplication().getCurrentUser().getId(), new Response<ItemsResult>() { @Override public void onResponse(ItemsResult response) { //First library and in-progress mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_library), new ViewQuery())); //Special suggestions String[] specialGenres = ThemeManager.getSpecialGenres(); if (specialGenres != null) { StdItemQuery suggestions = new StdItemQuery(); suggestions.setIncludeItemTypes(new String[]{"Movie", "Series"}); suggestions.setGenres(specialGenres); suggestions.setRecursive(true); suggestions.setLimit(40); suggestions.setSortBy(new String[]{ItemSortBy.DatePlayed}); suggestions.setSortOrder(SortOrder.Ascending); mRows.add(new BrowseRowDef(ThemeManager.getSuggestionTitle(), suggestions, 0, true, true, new ChangeTriggerType[]{})); } mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_continue_watching), getResumeQuery(), 0, true, true, new ChangeTriggerType[]{ChangeTriggerType.MoviePlayback, ChangeTriggerType.TvPlayback, ChangeTriggerType.VideoQueueChange}, QueryType.ContinueWatching)); //Now others based on first library type if (response.getTotalRecordCount() > 0) { String firstType = ("tvshows".equals(response.getItems()[0].getCollectionType())) ? "s" : ("livetv".equals(response.getItems()[0].getCollectionType()) ? "t" : "m"); switch (firstType) { case "s": addNextUp(); addPremieres(); addLatestMovies(); addOnNow(); break; case "t": addOnNow(); addNextUp(); addPremieres(); addLatestMovies(); break; default: addLatestMovies(); addNextUp(); addPremieres(); addOnNow(); } } // StdItemQuery latestMusic = new StdItemQuery(); // latestMusic.setIncludeItemTypes(new String[]{"MusicAlbum"}); // latestMusic.setRecursive(true); // latestMusic.setLimit(50); // latestMusic.setSortBy(new String[]{ItemSortBy.DateCreated}); // latestMusic.setSortOrder(SortOrder.Descending); // mRowDef.add(new BrowseRowDef("Latest Albums", latestMusic, 0)); rowLoader.loadRows(mRows); } }); } protected StdItemQuery getResumeQuery() { StdItemQuery resumeItems = new StdItemQuery(); resumeItems.setIncludeItemTypes(new String[]{"Movie", "Episode", "Video", "Program"}); resumeItems.setRecursive(true); resumeItems.setImageTypeLimit(1); resumeItems.setLimit(50); resumeItems.setFilters(new ItemFilter[]{ItemFilter.IsResumable}); resumeItems.setSortBy(new String[]{ItemSortBy.DatePlayed}); resumeItems.setSortOrder(SortOrder.Descending); return resumeItems; } protected void addLatestMovies() { StdItemQuery latestMovies = new StdItemQuery(); latestMovies.setIncludeItemTypes(new String[]{"Movie"}); latestMovies.setRecursive(true); latestMovies.setImageTypeLimit(1); latestMovies.setLimit(50); latestMovies.setCollapseBoxSetItems(false); if (TvApp.getApplication().getCurrentUser().getConfiguration().getHidePlayedInLatest()) latestMovies.setFilters(new ItemFilter[]{ItemFilter.IsUnplayed}); latestMovies.setSortBy(new String[]{ItemSortBy.DateCreated}); latestMovies.setSortOrder(SortOrder.Descending); mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_latest_movies), latestMovies, 0, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated, ChangeTriggerType.MoviePlayback})); } protected void addNextUp() { NextUpQuery nextUpQuery = new NextUpQuery(); nextUpQuery.setUserId(TvApp.getApplication().getCurrentUser().getId()); nextUpQuery.setImageTypeLimit(1); nextUpQuery.setLimit(50); nextUpQuery.setFields(new ItemFields[]{ItemFields.PrimaryImageAspectRatio, ItemFields.Overview}); mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_next_up_tv), nextUpQuery, new ChangeTriggerType[] {ChangeTriggerType.TvPlayback})); } protected void addPremieres() { StdItemQuery newQuery = new StdItemQuery(new ItemFields[]{ItemFields.DateCreated, ItemFields.PrimaryImageAspectRatio, ItemFields.Overview}); newQuery.setUserId(TvApp.getApplication().getCurrentUser().getId()); newQuery.setIncludeItemTypes(new String[]{"Episode"}); newQuery.setRecursive(true); newQuery.setIsVirtualUnaired(false); newQuery.setIsMissing(false); newQuery.setImageTypeLimit(1); newQuery.setFilters(new ItemFilter[]{ItemFilter.IsUnplayed}); newQuery.setSortBy(new String[]{ItemSortBy.DateCreated}); newQuery.setSortOrder(SortOrder.Descending); newQuery.setLimit(300); mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_new_premieres), newQuery, 0, true, true, new ChangeTriggerType[] {ChangeTriggerType.TvPlayback}, QueryType.Premieres)); } protected void addOnNow() { if (TvApp.getApplication().getCurrentUser().getPolicy().getEnableLiveTvAccess()) { RecommendedProgramQuery onNow = new RecommendedProgramQuery(); onNow.setIsAiring(true); onNow.setFields(new ItemFields[] {ItemFields.Overview, ItemFields.PrimaryImageAspectRatio, ItemFields.ChannelInfo}); onNow.setUserId(TvApp.getApplication().getCurrentUser().getId()); onNow.setImageTypeLimit(1); onNow.setLimit(20); mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_on_now), onNow)); //Latest Recordings RecordingQuery recordings = new RecordingQuery(); recordings.setFields(new ItemFields[]{ItemFields.Overview, ItemFields.PrimaryImageAspectRatio}); recordings.setUserId(TvApp.getApplication().getCurrentUser().getId()); recordings.setEnableImages(true); recordings.setLimit(40); mRows.add(new BrowseRowDef("Latest Recordings", recordings)); } } protected boolean hasResumeRow() { if (mRowsAdapter == null) return true; for (int i = 0; i < mRowsAdapter.size(); i++) { ListRow row = (ListRow)mRowsAdapter.get(i); if (row.getAdapter() instanceof ItemRowAdapter && ((ItemRowAdapter)row.getAdapter()).getQueryType().equals(QueryType.ContinueWatching)) return true; } return false; } protected void addContinueWatching() { //create the row and retrieve it to see if there are any before adding ItemRowAdapter resume = new ItemRowAdapter(getResumeQuery(), 0, true, true, mCardPresenter, mRowsAdapter, QueryType.ContinueWatching); resume.setReRetrieveTriggers(new ChangeTriggerType[] {ChangeTriggerType.VideoQueueChange, ChangeTriggerType.TvPlayback, ChangeTriggerType.MoviePlayback}); ListRow row = new ListRow(new HeaderItem(mApplication.getString(R.string.lbl_continue_watching)), resume); mRowsAdapter.add(1, row); resume.setRow(row); resume.Retrieve(); } protected AudioEventListener audioEventListener = new AudioEventListener() { @Override public void onQueueStatusChanged(boolean hasQueue) { //remove on any change - it will re-add on resume if (nowPlayingRow != null) { mRowsAdapter.remove(nowPlayingRow); nowPlayingRow = null; } } }; protected ListRow nowPlayingRow; protected void addNowPlaying() { if (MediaManager.isPlayingAudio()) { if (nowPlayingRow == null) { nowPlayingRow = new ListRow(new HeaderItem(getString(R.string.lbl_now_playing)), MediaManager.getManagedAudioQueue()); mRowsAdapter.add(1, nowPlayingRow); } } else { if (nowPlayingRow != null) { mRowsAdapter.remove(nowPlayingRow); nowPlayingRow = null; } } } @Override protected void addAdditionalRows(ArrayObjectAdapter rowAdapter) { super.addAdditionalRows(rowAdapter); HeaderItem gridHeader = new HeaderItem(rowAdapter.size(), mApplication.getString(R.string.lbl_settings)); GridButtonPresenter mGridPresenter = new GridButtonPresenter(); toolsRow = new ArrayObjectAdapter(mGridPresenter); toolsRow.add(new GridButton(SETTINGS, mApplication.getString(R.string.lbl_app_settings), R.drawable.gears)); toolsRow.add(new GridButton(LOGOUT, mApplication.getString(R.string.lbl_logout) + TvApp.getApplication().getCurrentUser().getName(), R.drawable.logout)); if (TvApp.getApplication().isConnectLogin()) toolsRow.add(new GridButton(LOGOUT_CONNECT, mApplication.getString(R.string.lbl_logout_connect), R.drawable.unlink)); //give this some time to have validated new Handler().postDelayed(new Runnable() { @Override public void run() { if (!TvApp.getApplication().isRegistered() && !TvApp.getApplication().isPaid()) { unlockButton = new GridButton(UNLOCK, mApplication.getString(R.string.lbl_unlock), R.drawable.unlock); toolsRow.add(unlockButton); } else if (!TvApp.getApplication().isRegistered()) { premiereButton = new GridButton(UNLOCK, mApplication.getString(R.string.btn_emby_premiere), R.drawable.embyicon); toolsRow.add(premiereButton); } } }, 5000); sendLogsButton = new GridButton(REPORT, mApplication.getString(R.string.lbl_send_logs), R.drawable.upload); rowAdapter.add(new ListRow(gridHeader, toolsRow)); } private void addLogsButton() { if (toolsRow != null && TvApp.getApplication().getPrefs().getBoolean("pref_enable_debug",false) && Utils.is50()) { if (toolsRow.indexOf(sendLogsButton) < 0) toolsRow.add(sendLogsButton); else if (toolsRow.indexOf(sendLogsButton) > -1) toolsRow.remove(sendLogsButton); } } @Override protected void setupEventListeners() { super.setupEventListeners(); mClickedListener.registerListener(new ItemViewClickedListener()); } private final class ItemViewClickedListener implements OnItemViewClickedListener { @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { if (item instanceof GridButton) { switch (((GridButton) item).getId()) { case LOGOUT: TvApp app = TvApp.getApplication(); if (app.getIsAutoLoginConfigured()) { // Present user selection app.setLoginApiClient(app.getApiClient()); Intent userIntent = new Intent(getActivity(), SelectUserActivity.class); userIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); getActivity().startActivity(userIntent); } getActivity().finish(); //don't actually log out because we handle it ourselves break; case SETTINGS: Intent settings = new Intent(getActivity(), SettingsActivity.class); getActivity().startActivity(settings); break; case UNLOCK: Intent unlock = new Intent(getActivity(), UnlockActivity.class); getActivity().startActivity(unlock); break; case REPORT: Utils.reportError(getActivity(), "Send Log to Dev"); break; case LOGOUT_CONNECT: TvApp.getApplication().getConnectionManager().Logout(new EmptyResponse() { @Override public void onResponse() { mApplication.setConnectLogin(false); getActivity().finish(); } }); break; default: Toast.makeText(getActivity(), item.toString(), Toast.LENGTH_SHORT) .show(); break; } } } } }