package com.felkertech.cumulustv.tv.fragments;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.graphics.drawable.Drawable;
import android.media.tv.TvContract;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.Toast;
import com.felkertech.cumulustv.activities.SettingsActivity;
import com.felkertech.cumulustv.fileio.CloudStorageProvider;
import com.felkertech.cumulustv.plugins.CumulusChannel;
import com.felkertech.cumulustv.plugins.CumulusTvPlugin;
import com.felkertech.cumulustv.plugins.JsonListingPanelActivity;
import com.felkertech.cumulustv.plugins.ListingPlugin;
import com.felkertech.cumulustv.plugins.MainPicker;
import com.felkertech.cumulustv.services.CumulusJobService;
import com.felkertech.cumulustv.utils.ActivityUtils;
import com.felkertech.cumulustv.utils.DriveSettingsManager;
import com.felkertech.n.cumulustv.R;
import com.felkertech.cumulustv.model.ChannelDatabase;
import com.felkertech.cumulustv.model.JsonChannel;
import com.felkertech.cumulustv.model.Option;
import com.felkertech.cumulustv.model.SuggestedChannels;
import com.felkertech.cumulustv.receivers.GoogleDriveBroadcastReceiver;
import com.felkertech.cumulustv.tv.Utils;
import com.felkertech.cumulustv.tv.activities.DetailsActivity;
import com.felkertech.cumulustv.tv.presenters.CardPresenter;
import com.felkertech.cumulustv.tv.presenters.OptionsCardPresenter;
import com.google.android.exoplayer2.C;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.drive.Drive;
import com.google.android.gms.drive.DriveApi;
import com.google.android.gms.drive.MetadataChangeSet;
import com.google.android.media.tv.companionlibrary.EpgSyncJobService;
import org.json.JSONException;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class LeanbackFragment extends BrowseFragment implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = LeanbackFragment.class.getSimpleName();
private static final boolean DEBUG = true;
private static final int BACKGROUND_UPDATE_DELAY = 300;
public static final int REQUEST_CODE_CREATOR = 102;
private static final int ORDER_GENRE_CATEGORY = 0;
private static final int ORDER_ALL_CHANNELS = 1;
private static final int ORDER_SUGGESTED_CHANNELS = 2;
private static final int ORDER_GOOGLE_DRIVE = 3;
private static final int ORDER_MANAGE_SETTINGS = 4;
private final Handler mHandler = new Handler();
private ArrayObjectAdapter mRowsAdapter;
private Drawable mDefaultBackground;
private DisplayMetrics mMetrics;
private Timer mBackgroundTimer;
private BackgroundManager mBackgroundManager;
private DriveSettingsManager sm;
public Activity mActivity;
private final GoogleDriveBroadcastReceiver broadcastReceiver =
new GoogleDriveBroadcastReceiver() {
@Override
public void onDownloadCompleted() {
refreshUI();
Toast.makeText(mActivity, R.string.toast_sync_data_downloaded, Toast.LENGTH_SHORT).show();
}
@Override
public void onUploadCompleted() {
refreshUI(); // Probably need to reload anyway
Toast.makeText(mActivity, R.string.toast_sync_data_uploaded, Toast.LENGTH_SHORT).show();
}
@Override
public void onNetworkActionCompleted() {
if (DEBUG) {
Log.d(TAG, "Some network action occurred");
}
}
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onActivityCreated(savedInstanceState);
sm = new DriveSettingsManager(getActivity());
CloudStorageProvider.getInstance().autoConnect(getActivity());
refreshUI();
}
@Override
public void onDestroy() {
super.onDestroy();
if (null != mBackgroundTimer) {
Log.d(TAG, "onDestroy: " + mBackgroundTimer.toString());
mBackgroundTimer.cancel();
}
// LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcastReceiver);
}
@Override
public void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(broadcastReceiver,
new IntentFilter(GoogleDriveBroadcastReceiver.ACTION_STATUS_CHANGED));
Log.d(TAG, "Registered broadcast receiver");
}
@Override
public void onResume() {
super.onResume();
mBackgroundManager.setDrawable(getResources().getDrawable(R.drawable.c_background5));
}
public void refreshUI() {
prepareBackgroundManager();
setupUIElements();
loadRows();
setupEventListeners();
}
private void loadRows() {
// Here are my rows
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
if (mActivity == null) {
mActivity = getActivity();
}
// My channels
if(mActivity == null && Build.VERSION.SDK_INT >= 23) {
Toast.makeText(getContext(), R.string.toast_error_no_activity,
Toast.LENGTH_SHORT).show();
return;
} else if(mActivity == null) {
return;
}
ChannelDatabase cd;
try {
cd = ChannelDatabase.getInstance(mActivity);
} catch (ChannelDatabase.MalformedChannelDataException e) {
ActivityUtils.handleMalformedChannelData(getActivity(),
CloudStorageProvider.getInstance().getClient(), e);
return;
}
Map<String, ListRow> genresListRows = new HashMap<>();
try {
CardPresenter channelCardPresenter = new CardPresenter();
ArrayObjectAdapter channelRowAdapter = new ArrayObjectAdapter(channelCardPresenter);
int index = 0;
for(JsonChannel jsonChannel : cd.getJsonChannels()) {
if (DEBUG) {
Log.d(TAG, "Got channels " + jsonChannel.getName());
}
String[] genres = jsonChannel.getGenres();
for (String genre : genres) {
if (!genresListRows.containsKey(genre)) {
CardPresenter genresCardPresenter = new CardPresenter();
ArrayObjectAdapter genresObjectAdapter = new ArrayObjectAdapter(genresCardPresenter);
HeaderItem genresHeader =
new HeaderItem(ORDER_GENRE_CATEGORY, Utils.normalizeGenre(genre));
ListRow row = new ListRow(genresHeader, genresObjectAdapter);
mRowsAdapter.add(row);
genresListRows.put(genre, row);
}
((ArrayObjectAdapter) genresListRows.get(genre).getAdapter()).add(jsonChannel);
}
channelRowAdapter.add(jsonChannel);
index++;
}
HeaderItem header = new HeaderItem(ORDER_ALL_CHANNELS, getString(R.string.my_channels));
mRowsAdapter.add(new ListRow(header, channelRowAdapter));
} catch (JSONException e) {
e.printStackTrace();
}
// Second row is suggested channels
CardPresenter suggestedChannelPresenter = new CardPresenter();
ArrayObjectAdapter suggestedChannelAdapter =
new ArrayObjectAdapter(suggestedChannelPresenter);
HeaderItem suggestedChannelsHeader = new HeaderItem(ORDER_SUGGESTED_CHANNELS,
getString(R.string.suggested_channels));
CumulusChannel[] suggestedChannels = SuggestedChannels.getSuggestedChannels();
for(CumulusChannel jsonChannel : suggestedChannels) {
suggestedChannelAdapter.add(jsonChannel);
}
mRowsAdapter.add(new ListRow(suggestedChannelsHeader, suggestedChannelAdapter));
// Third row is Drive
HeaderItem driveHeader = new HeaderItem(ORDER_GOOGLE_DRIVE, getString(R.string.google_drive_sync));
OptionsCardPresenter drivePresenter = new OptionsCardPresenter();
ArrayObjectAdapter driveAdapter = new ArrayObjectAdapter(drivePresenter);
driveAdapter.add(new Option(getResources().getDrawable(R.drawable.ic_google_drive),
getString(R.string.connect_drive)));
driveAdapter.add(new Option(getResources().getDrawable(R.drawable.ic_cloud_download),
getString(R.string.settings_refresh_cloud_local)));
driveAdapter.add(new Option(getResources().getDrawable(R.drawable.ic_google_drive_folder),
getString(R.string.settings_switch_google_drive)));
mRowsAdapter.add(new ListRow(driveHeader, driveAdapter));
// Fourth row is actions
HeaderItem gridHeader = new HeaderItem(ORDER_MANAGE_SETTINGS, getString(R.string.manage));
OptionsCardPresenter mGridPresenter = new OptionsCardPresenter();
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
gridRowAdapter.add(new Option(getResources().getDrawable(R.drawable.ic_television),
getString(R.string.manage_livechannels)));
gridRowAdapter.add(new Option(getResources().getDrawable(R.drawable.ic_add_channel),
getString(R.string.manage_add_new)));
gridRowAdapter.add(new Option(getResources().getDrawable(R.drawable.ic_link_channel),
getString(R.string.add_jsonlisting)));
gridRowAdapter.add(new Option(getResources().getDrawable(R.drawable.ic_apps),
getString(R.string.installed_plugins)));
mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
// Settings will become its own activity
HeaderItem gridHeader2 = new HeaderItem(ORDER_MANAGE_SETTINGS, getString(R.string.settings));
OptionsCardPresenter mGridPresenter2 = new OptionsCardPresenter();
ArrayObjectAdapter gridRowAdapter2 = new ArrayObjectAdapter(mGridPresenter2);
/*
gridRowAdapter2.add(new Option(getResources().getDrawable(R.drawable.ic_animation),
getString(R.string.settings_browse_plugins)));
*/
gridRowAdapter2.add(new Option(getResources().getDrawable(R.drawable.ic_book_open),
getString(R.string.settings_view_licenses)));
gridRowAdapter2.add(new Option(getResources().getDrawable(R.drawable.ic_delete_forever),
getString(R.string.settings_reset_channel_data)));
gridRowAdapter2.add(new Option(getResources().getDrawable(R.drawable.ic_help_circle_fill),
getString(R.string.about_app)));
gridRowAdapter2.add(new Option(getResources().getDrawable(R.drawable.ic_cog),
getString(R.string.settings)));
mRowsAdapter.add(new ListRow(gridHeader2, gridRowAdapter2));
setAdapter(mRowsAdapter);
}
private void prepareBackgroundManager() {
try {
mBackgroundManager = BackgroundManager.getInstance(getActivity());
mBackgroundManager.attach(getActivity().getWindow());
mDefaultBackground = getResources().getDrawable(R.drawable.c_background5);
mBackgroundManager.setDrawable(getResources().getDrawable(R.drawable.c_background5));
mMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
} catch(Exception ignored) {
}
}
private void setupUIElements() {
try {
setBadgeDrawable(getActivity().getResources().getDrawable(R.mipmap.ic_launcher));
// setTitle(getString(R.string.app_name)); // Badge, when set, takes precedent
// over title
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
// set fastLane (or headers) background color
setBrandColor(getResources().getColor(R.color.colorPrimary));
mBackgroundManager.setDrawable(getResources().getDrawable(R.drawable.c_background5));
// set search icon color
// setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
} catch(Exception ignored) {
}
}
private void setupEventListeners() {
/*setOnSearchClickedListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getActivity(), "Implement your own in-app search", Toast.LENGTH_LONG)
.show();
}
});*/
setOnItemViewClickedListener(new ItemViewClickedListener());
setOnItemViewSelectedListener(new ItemViewSelectedListener());
}
@Override
public void onConnected(Bundle bundle) {
ActivityUtils.onConnected(CloudStorageProvider.getInstance().getClient());
Log.d(TAG, "onConnected");
sm.setGoogleDriveSyncable(CloudStorageProvider.getInstance().getClient(), new DriveSettingsManager.GoogleDriveListener() {
@Override
public void onActionFinished(boolean cloudToLocal) {
Log.d(TAG, "Sync req after drive action");
final String info = TvContract.buildInputId(ActivityUtils.TV_INPUT_SERVICE);
CumulusJobService.requestImmediateSync1(mActivity, info, CumulusJobService.DEFAULT_IMMEDIATE_EPG_DURATION_MILLIS,
new ComponentName(mActivity, CumulusJobService.class));
if (cloudToLocal) {
Toast.makeText(getActivity(), R.string.download_complete, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), R.string.upload_complete, Toast.LENGTH_SHORT).show();
}
}
}); //Enable GDrive
Log.d(TAG, sm.getString(R.string.sm_google_drive_id) + "<< for onConnected");
if(sm.getString(R.string.sm_google_drive_id).isEmpty()) {
//We need a new file
ActivityUtils.createDriveData(mActivity, CloudStorageProvider.getInstance().getClient(),
driveContentsCallback);
} else {
//Great, user already has sync enabled, let's resync
ActivityUtils.readDriveData(mActivity, CloudStorageProvider.getInstance().getClient());
Handler h = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
refreshUI();
}
};
h.sendEmptyMessageDelayed(0, 4000);
}
}
ResultCallback<DriveApi.DriveContentsResult> driveContentsCallback =
new ResultCallback<DriveApi.DriveContentsResult>() {
@Override
public void onResult(DriveApi.DriveContentsResult result) {
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setTitle("cumulustv_channels.json")
.setDescription("JSON list of channels that can be imported using CumulusTV to view live streams")
.setMimeType("application/json").build();
IntentSender intentSender = Drive.DriveApi
.newCreateFileActivityBuilder()
.setActivityTitle("cumulustv_channels.json")
.setInitialMetadata(metadataChangeSet)
.setInitialDriveContents(result.getDriveContents())
.build(CloudStorageProvider.getInstance().getClient());
try {
mActivity.startIntentSenderForResult(
intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.w(TAG, "Unable to send intent", e);
}
}
};
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if (DEBUG) {
Log.d(TAG, "Error connecting " + connectionResult.getErrorCode());
Log.d(TAG, "oCF " + connectionResult.toString());
}
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(mActivity, ActivityUtils.RESOLVE_CONNECTION_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
// Unable to resolve, message user appropriately
}
} else {
GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), mActivity, 0).show();
}
}
private final class ItemViewClickedListener implements OnItemViewClickedListener {
@Override
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof JsonChannel) {
JsonChannel jsonChannel = (JsonChannel) item;
Log.d(TAG, "Item: " + item.toString());
Intent intent = new Intent(mActivity, DetailsActivity.class);
intent.putExtra(VideoDetailsFragment.EXTRA_JSON_CHANNEL, jsonChannel.toString());
/*Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
mActivity,
((ImageCardView) itemViewHolder.view).getMainImageView(),
DetailsActivity.SHARED_ELEMENT_NAME).toBundle();*/
mActivity.startActivity(intent, null);
} else if (item instanceof Option) {
String title = ((Option) item).getText();
if(title.equals(getString(R.string.manage_livechannels))) {
ActivityUtils.launchLiveChannels(mActivity);
} else if(title.equals(getString(R.string.manage_add_suggested))) {
ActivityUtils.openSuggestedChannels(mActivity,
CloudStorageProvider.getInstance().getClient());
} else if(title.equals(getString(R.string.manage_add_new))) {
Intent i = new Intent(getActivity(), MainPicker.class);
i.putExtra(CumulusTvPlugin.INTENT_EXTRA_ACTION, CumulusTvPlugin.INTENT_ADD);
startActivity(i);
} else if (title.equals(getString(R.string.add_jsonlisting))) {
Intent i = new Intent(getActivity(), JsonListingPanelActivity.class);
startActivity(i);
} else if (title.equals(getString(R.string.installed_plugins))) {
ActivityUtils.openPluginPicker(true, mActivity);
} else if(title.equals(getString(R.string.connect_drive))) {
CloudStorageProvider.getInstance().connect(mActivity);
} else if(title.equals(getString(R.string.settings_switch_google_drive))) {
CloudStorageProvider.getInstance().pickDriveFile(mActivity);
} else if(title.equals(getString(R.string.settings_browse_plugins))) {
ActivityUtils.browsePlugins(mActivity);
} else if(title.equals(getString(R.string.settings_refresh_cloud_local))) {
CloudStorageProvider.getInstance().connect(mActivity);
ActivityUtils.readDriveData(mActivity,
CloudStorageProvider.getInstance().getClient());
} else if(title.equals(getString(R.string.settings_view_licenses))) {
ActivityUtils.oslClick(mActivity);
} else if(title.equals(getString(R.string.settings_reset_channel_data))) {
ActivityUtils.deleteChannelData(mActivity,
CloudStorageProvider.getInstance().getClient());
} else if(title.equals(getString(R.string.about_app))) {
ActivityUtils.openAbout(mActivity);
} else if (title.equals(getString(R.string.settings))) {
Intent i = new Intent(getActivity(), SettingsActivity.class);
startActivity(i);
} else {
Toast.makeText(mActivity, ((String) item), Toast.LENGTH_SHORT)
.show();
}
}
}
}
private final class ItemViewSelectedListener implements OnItemViewSelectedListener {
@Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof JsonChannel) {
startBackgroundTimer();
}
}
}
private void startBackgroundTimer() {
if (null != mBackgroundTimer) {
mBackgroundTimer.cancel();
}
mBackgroundTimer = new Timer();
mBackgroundTimer.schedule(new UpdateBackgroundTask(), BACKGROUND_UPDATE_DELAY);
}
private class UpdateBackgroundTask extends TimerTask {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
}
});
}
}
}