/*
Copyright 2014 John Selbie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.selbie.wrek;
import java.util.ArrayList;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ListFragment;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.selbie.wrek.ScheduleFetcher.ScheduleFetcherCallback;
public class ScheduleListFragment extends ListFragment implements ScheduleFetcherCallback
{
private static final String TAG = "ScheduleListFragment";
private static final int BITRATE_HIGH_KBIT_SEC = 128;
private static final int BITRATE_LOW_KBIT_SEC = 24;
private ArrayList<ScheduleItem> _list;
@Override
public void onCreate(Bundle savedInstanceState)
{
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
ScheduleFetcher fetcher = ScheduleFetcher.getInstance();
fetcher.attachObserver(this);
// note - the view has not been inflated at this point
_list = fetcher.getLastestSchedule(); // this will trigger a refresh if needed
onNewSchedule(_list);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.schedule_list, container, false);
return view;
}
@Override public void onStart()
{
super.onStart();
// this is for a corner case. If we navigated back to the MainActivity without it being previously destroyed,
// but never had a schedule to begin with (or the current one is expired), then force a refresh
configureSpinnerControl(true);
ScheduleFetcher fetcher = ScheduleFetcher.getInstance();
fetcher.startRefreshIfNeeded();
}
@Override
public void onDestroy()
{
Log.d(TAG, "onDestroy");
super.onDestroy();
ScheduleFetcher.getInstance().detachObserver(this);
}
@Override
public void onNewSchedule(ArrayList<ScheduleItem> schedule)
{
ScheduleListAdapter adapter = new ScheduleListAdapter(getActivity(), schedule);
this.setListAdapter(adapter);
}
@Override
public void onScheduleDownloadError(int errorcode)
{
// handle the failure case
// If we already have a non-empty schedule, then we'll just keep using it
// we can decide later if we want to show an error message elsewhere...
// If we can't get a schedule - then build a provisional schedule consisting only of the live streams
if (_list.size() > 0)
{
Log.d(TAG, "onError - reusing existing schedule since it is non-empty");
}
else
{
Log.d(TAG, "onError - can't get schedule!");
configureSpinnerControl(false); // put the empty view into "error mode"
}
}
private void configureSpinnerControl(boolean spinning)
{
View view = getView();
// check to make sure that the view is still inflated.
if (view == null)
{
return;
}
TextView tv = (TextView)(getView().findViewById(R.id.spinnerText));
ProgressBar progbar = (ProgressBar)(getView().findViewById(R.id.spinner));
if (spinning)
{
tv.setText(R.string.downloading_schedule);
progbar.setVisibility(View.VISIBLE);
}
else
{
tv.setText(R.string.downloading_schedule_error);
progbar.setVisibility(View.GONE);
}
}
@Override
public void onListItemClick(ListView listview, View view, int position, long id)
{
ScheduleItem item = (ScheduleItem) (getListAdapter().getItem(position));
int targetBitrate = getTargetBitrate();
Stream stream = item.getStreamForAllowedBitrate(targetBitrate);
if ((stream != null) && (stream.getPlayList().size() > 0))
{
MediaPlayerPresenter presenter = MediaPlayerPresenter.getInstance();
Log.d(TAG, "Setting playlist for item " + position + " (" + item.getTitle() + ")");
presenter.setPlaylist(item.getTitle(), stream.getPlayList(), stream.getIsLiveStream(), stream.getHasIcyMetaInt());
}
else
{
Log.e(TAG, "No stream or playlist for selected item!!!");
}
}
@TargetApi(16)
private int getTargetBitrate()
{
ConnectivityManager connManager = (ConnectivityManager) getActivity().getSystemService(Activity.CONNECTIVITY_SERVICE);
boolean isWifi = false;
boolean isMobile = false;
boolean isEthernet = false;
boolean isRoaming = false;
boolean isMetered = false;
boolean isConnected = false;
int result = 0;
int bandwidth_settings = SettingsFragment.getBitrateSetting(getActivity());
switch (bandwidth_settings)
{
case SettingsFragment.BITRATE_SETTINGS_HIGH:
{
Log.d(TAG, "Using high bitrate settings");
result = BITRATE_HIGH_KBIT_SEC;
break;
}
case SettingsFragment.BITRATE_SETTINGS_LOW:
{
Log.d(TAG, "Using low bitrate settings");
result = BITRATE_LOW_KBIT_SEC;
break;
}
default:
{
result = 0;
break;
}
}
if (result == 0)
{
// default to low-bitrate unless we specifically detect we are on a
// non-mobile connection
result = BITRATE_LOW_KBIT_SEC;
Log.d(TAG, "Using automatic bandwidth settings");
NetworkInfo netinfo = connManager.getActiveNetworkInfo();
if (netinfo != null)
{
int nettype = netinfo.getType();
isWifi = (nettype == ConnectivityManager.TYPE_WIFI);
isEthernet = (nettype == ConnectivityManager.TYPE_ETHERNET);
isMobile = ( (nettype == ConnectivityManager.TYPE_MOBILE) ||
(nettype == ConnectivityManager.TYPE_MOBILE_DUN) ||
(nettype == ConnectivityManager.TYPE_MOBILE_HIPRI) ||
(nettype == ConnectivityManager.TYPE_MOBILE_MMS) ||
(nettype == ConnectivityManager.TYPE_MOBILE_SUPL) );
isConnected = netinfo.isConnected();
isRoaming = netinfo.isRoaming();
Log.d(TAG, "active network type = " + netinfo.getTypeName());
Log.d(TAG, "isConnected = " + isConnected);
Log.d(TAG, "isWifi = " + isWifi);
Log.d(TAG, "isEthernet = " + isEthernet);
Log.d(TAG, "isMobile = " + isMobile);
Log.d(TAG, "isRoaming = " + isRoaming);
}
// isActiveNetworkMetered was introduced in API 16 (Jelly Bean)
if (android.os.Build.VERSION.SDK_INT >= 16)
{
isMetered = connManager.isActiveNetworkMetered();
Log.d(TAG, "isMetered = " + isMetered);
}
if (isWifi || isEthernet)
{
result = BITRATE_HIGH_KBIT_SEC;
}
else if (isMobile==false)
{
Log.w(TAG, "Network is neither Wifi, Ethernet, nor Mobile. Selecting stream bitrate based on metered/roaming state");
Log.w(TAG, "Its possible there is no connection active");
if (!isMetered && !isRoaming)
{
Log.w(TAG, "Default to high bitrate since metered/roaming flags are false");
result = BITRATE_HIGH_KBIT_SEC;
}
}
}
Log.d(TAG, "getTargetBitrate() returns " + result + "kbit/sec");
return result;
}
}