blob: 6abef980e88ee450823acdb456e9c288a2cc0150 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.android.fmradio;
import android.app.ActionBar;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.location.Location;
import android.location.LocationManager;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.android.fmradio.FmService.OnExitListener;
import com.android.fmradio.FmStation.Station;
/**
* This class interact with user, provider edit station information, such as add
* to favorite, edit favorite, delete from favorite
*/
public class FmFavoriteActivity extends Activity {
// Logging
private static final String TAG = "FmFavoriteActivity";
public static final String ACTIVITY_RESULT = "ACTIVITY_RESULT";
private static final String SHOW_GPS_DIALOG = "SHOW_GPS_DIALOG";
private static final String GPS_NOT_LOCATED_DIALOG = "GPS_NOT_LOCATED_DIALOG";
private ListView mLvFavorites = null; // list view
LinearLayout mSearchTips = null;
private Context mContext = null; // application context
private OnExitListener mExitListener = null;
private MyFavoriteAdapter mMyAdapter;
private ProgressBar mSearchProgress = null;
private MenuItem mMenuRefresh = null;
private LocationManager mLocationManager;
private Location mCurLocation;
private boolean mIsActivityForeground = true;
/**
* on create
*
* @param savedInstanceState The save instance state
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Bind the activity to FM audio stream.
setVolumeControlStream(AudioManager.STREAM_MUSIC);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
setContentView(R.layout.favorite);
// display action bar and navigation button
ActionBar actionBar = getActionBar();
actionBar.setTitle(getString(R.string.station_title));
actionBar.setDisplayHomeAsUpEnabled(true);
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mContext = getApplicationContext();
mMyAdapter = new MyFavoriteAdapter(mContext);
mLvFavorites = (ListView) findViewById(R.id.station_list);
mSearchTips = (LinearLayout) findViewById(R.id.search_tips);
mSearchProgress = (ProgressBar) findViewById(R.id.search_progress);
mLvFavorites.setAdapter(mMyAdapter); // set adapter
mMyAdapter.swipResult(getData());
mLvFavorites.setOnItemClickListener(new AdapterView.OnItemClickListener() {
/**
* Click list item will finish activity and pass value to other activity
*
* @param parent adapter view
* @param view item view
* @param position current position
* @param id current id
*/
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Set the selected frequency to main UI and finish the
// favorite manager.
TextView textView = (TextView) view.findViewById(R.id.lv_station_freq);
float frequency = 0;
try {
frequency = Float.parseFloat(textView.getText().toString());
} catch (NumberFormatException e) {
e.printStackTrace();
}
Intent intentResult = new Intent();
intentResult.putExtra(ACTIVITY_RESULT, FmUtils.computeStation(frequency));
setResult(RESULT_OK, intentResult);
finish();
}
});
// Finish favorite when exit FM
mExitListener = new FmService.OnExitListener() {
@Override
public void onExit() {
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(new Runnable() {
@Override
public void run() {
FmFavoriteActivity.this.finish();
}
});
}
};
FmService.registerExitListener(mExitListener);
if (!mIsServiceBinded) {
bindService();
}
}
/**
* When menu is selected
*
* @param item The selected menu item
* @return true to consume it, false to can handle other
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
case R.id.fm_station_list_refresh:
if (null != mService) {
refreshMenuItem(false);
mMyAdapter.swipResult(null);
mLvFavorites.setEmptyView(mSearchTips);
mSearchProgress.setIndeterminate(true);
// If current location and last location exceed defined distance, delete the RDS database
if (isGpsOpen()) {
mCurLocation = mLocationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (mCurLocation != null) {
double[] lastLocations = FmUtils.getLastSearchedLocation(mContext);
float distance[] = new float[2];
Location.distanceBetween(lastLocations[0], lastLocations[1],
mCurLocation.getLatitude(), mCurLocation.getLongitude(),
distance);
float searchedDistance = distance[0];
boolean exceed =
searchedDistance > FmUtils.LOCATION_DISTANCE_EXCEED;
mService.setDistanceExceed(exceed);
FmUtils.setLastSearchedLocation(mContext, mCurLocation.getLatitude(),
mCurLocation.getLongitude());
}
}
mService.startScanAsync();
}
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
if (null == mService) {
Log.w(TAG, "onBackPressed, mService is null");
} else {
boolean isScanning = mService.isScanning();
if (isScanning) {
mService.stopScan();
}
}
super.onBackPressed();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.fm_station_list_menu, menu);
mMenuRefresh = menu.findItem(R.id.fm_station_list_refresh);
resetMenuTitleColor(true);
return true;
}
private void resetMenuTitleColor(boolean enabled) {
SpannableString ss = new SpannableString(mMenuRefresh.getTitle());
int titleColor = enabled ? getResources().getColor(R.color.actionbar_overflow_title_color)
: getResources().getColor(R.color.actionbar_overflow_title_disabled_color);
ss.setSpan(new ForegroundColorSpan(titleColor), 0, ss.length(), 0);
mMenuRefresh.setTitle(ss);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (null != mService) {
boolean isScan = mService.isScanning();
refreshMenuItem(!isScan);
}
return super.onPrepareOptionsMenu(menu);
}
static final class ViewHolder {
ImageView mStationTypeView;
TextView mStationFreqView;
TextView mStationNameView;
TextView mStationRdsView;
RelativeLayout mImgLayout;
}
private Cursor getData() {
Cursor cursor = mContext.getContentResolver().query(Station.CONTENT_URI,
FmStation.COLUMNS, null, null, FmStation.Station.FREQUENCY);
return cursor;
}
class MyFavoriteAdapter extends BaseAdapter {
private Cursor mCursor;
private LayoutInflater mInflater;
public MyFavoriteAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
public void swipResult(Cursor cursor) {
if (null != mCursor) {
mCursor.close();
}
mCursor = cursor;
notifyDataSetChanged();
}
@Override
public int getCount() {
if (null != mCursor) {
return mCursor.getCount();
}
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (null == convertView) {
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.station_item, null);
viewHolder.mStationTypeView = (ImageView) convertView
.findViewById(R.id.lv_station_type);
viewHolder.mStationFreqView = (TextView) convertView
.findViewById(R.id.lv_station_freq);
viewHolder.mStationNameView = (TextView) convertView
.findViewById(R.id.lv_station_name);
viewHolder.mStationRdsView = (TextView) convertView
.findViewById(R.id.lv_station_rds);
viewHolder.mImgLayout = (RelativeLayout) convertView
.findViewById(R.id.list_item_right);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (mCursor != null && mCursor.moveToFirst()) {
mCursor.moveToPosition(position);
final int stationFreq = mCursor.getInt(mCursor
.getColumnIndex(FmStation.Station.FREQUENCY));
String name = mCursor.getString(mCursor
.getColumnIndex(FmStation.Station.STATION_NAME));
String rds = mCursor.getString(mCursor
.getColumnIndex(FmStation.Station.RADIO_TEXT));
final int isFavorite = mCursor.getInt(mCursor
.getColumnIndex(FmStation.Station.IS_FAVORITE));
if (null == name || "".equals(name)) {
name = mCursor.getString(mCursor
.getColumnIndex(FmStation.Station.PROGRAM_SERVICE));
}
if (rds == null || rds.equals("")) {
viewHolder.mStationRdsView.setVisibility(View.GONE);
} else {
viewHolder.mStationRdsView.setVisibility(View.VISIBLE);
}
viewHolder.mStationFreqView.setText(FmUtils.formatStation(stationFreq));
viewHolder.mStationNameView.setText(name);
viewHolder.mStationRdsView.setText(rds);
if (0 == isFavorite) {
viewHolder.mStationTypeView.setImageResource(R.drawable.btn_fm_favorite_off);
viewHolder.mStationTypeView.setColorFilter(Color.BLACK,
PorterDuff.Mode.SRC_ATOP);
viewHolder.mStationTypeView.setAlpha(0.54f);
} else {
viewHolder.mStationTypeView.setImageResource(R.drawable.btn_fm_favorite_on);
viewHolder.mStationTypeView.setColorFilter(null);
viewHolder.mStationTypeView.setAlpha(1.0f);
}
viewHolder.mImgLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (0 == isFavorite) {
addFavorite(stationFreq);
} else {
deleteFavorite(stationFreq);
}
}
});
}
return convertView;
}
}
/**
* Add searched station as favorite station
*/
public void addFavorite(int stationFreq) {
// TODO it's on UI thread, change to sub thread
// update the station name and station type in database
// according the frequency
FmStation.addToFavorite(mContext, stationFreq);
mMyAdapter.swipResult(getData());
}
/**
* Delete favorite from favorite station list, make it as searched station
*/
public void deleteFavorite(int stationFreq) {
// TODO it's on UI thread, change to sub thread
// update the station type from favorite to searched.
FmStation.removeFromFavorite(mContext, stationFreq);
mMyAdapter.swipResult(getData());
}
@Override
protected void onResume() {
super.onResume();
mIsActivityForeground = true;
if (null != mService) {
mService.setFmMainActivityForeground(mIsActivityForeground);
if (FmRecorder.STATE_RECORDING != mService.getRecorderState()) {
mService.removeNotification();
}
}
}
@Override
protected void onPause() {
mIsActivityForeground = false;
if (null != mService) {
mService.setFmMainActivityForeground(mIsActivityForeground);
}
super.onPause();
}
@Override
protected void onStop() {
// FmUtils.updateFrontActivity(mContext, "");
if (null != mService) {
// home key pressed, show notification
mService.setNotificationClsName(FmFavoriteActivity.class.getName());
mService.updatePlayingNotification();
}
super.onStop();
}
@Override
protected void onDestroy() {
mMyAdapter.swipResult(null);
FmService.unregisterExitListener(mExitListener);
if (mService != null) {
mService.unregisterFmRadioListener(mFmRadioListener);
}
unbindService();
super.onDestroy();
}
private FmService mService = null;
private boolean mIsServiceBinded = false;
private void bindService() {
mIsServiceBinded = bindService(new Intent(FmFavoriteActivity.this, FmService.class),
mServiceConnection, Context.BIND_AUTO_CREATE);
if (!mIsServiceBinded) {
Log.e(TAG, "bindService, mIsServiceBinded is false");
finish();
}
}
private void unbindService() {
if (mIsServiceBinded) {
unbindService(mServiceConnection);
}
}
// Service listener
private FmListener mFmRadioListener = new FmListener() {
@Override
public void onCallBack(Bundle bundle) {
int flag = bundle.getInt(FmListener.CALLBACK_FLAG);
if (flag == FmListener.MSGID_FM_EXIT) {
mHandler.removeCallbacksAndMessages(null);
}
// remove tag message first, avoid too many same messages in queue.
Message msg = mHandler.obtainMessage(flag);
msg.setData(bundle);
mHandler.removeMessages(flag);
mHandler.sendMessage(msg);
}
};
/**
* Main thread handler to update UI
*/
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d(TAG,
"handleMessage, what = " + msg.what + ",hashcode:"
+ mHandler.hashCode());
Bundle bundle;
switch (msg.what) {
case FmListener.MSGID_SCAN_FINISHED:
bundle = msg.getData();
// cancel scan happen
boolean isScan = bundle.getBoolean(FmListener.KEY_IS_SCAN);
int searchedNum = bundle.getInt(FmListener.KEY_STATION_NUM);
refreshMenuItem(true);
mMyAdapter.swipResult(getData());
mService.updatePlayingNotification();
if (searchedNum == 0) {
Toast.makeText(mContext, getString(R.string.toast_cannot_search),
Toast.LENGTH_SHORT).show();
// searched station is zero, if db has station, should not use empty.
if (mMyAdapter.getCount() == 0) {
View emptyView = (View) findViewById(R.id.empty_tips);
emptyView.setVisibility(View.VISIBLE);
View searchTips = (View) findViewById(R.id.search_tips);
searchTips.setVisibility(View.GONE);
}
return;
}
// Show toast to tell user how many stations have been searched
String text = getString(R.string.toast_station_searched) + " "
+ String.valueOf(searchedNum);
Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
break;
case FmListener.MSGID_SWITCH_ANTENNA:
bundle = msg.getData();
boolean isHeadset = bundle.getBoolean(FmListener.KEY_IS_SWITCH_ANTENNA);
// if receive headset plugout, need set headset mode on ui
if (!isHeadset) {
finish();
}
break;
default:
break;
}
}
};
private void refreshMenuItem(boolean enabled) {
// action menu
if (mMenuRefresh != null) {
mMenuRefresh.setEnabled(enabled);
resetMenuTitleColor(enabled);
}
}
// When call bind service, it will call service connect. register call back
// listener and initial device
private final ServiceConnection mServiceConnection = new ServiceConnection() {
/**
* called by system when bind service
*
* @param className component name
* @param service service binder
*/
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mService = ((FmService.ServiceBinder) service).getService();
if (null == mService) {
Log.e(TAG, "onServiceConnected, mService is null");
finish();
return;
}
mService.registerFmRadioListener(mFmRadioListener);
mService.setFmMainActivityForeground(mIsActivityForeground);
if (FmRecorder.STATE_RECORDING != mService.getRecorderState()) {
mService.removeNotification();
}
// FmUtils.isFirstEnterStationList() must be called at the first time.
// After it is called, it will save status to SharedPreferences.
if (FmUtils.isFirstEnterStationList(mContext) || (0 == mMyAdapter.getCount())) {
refreshMenuItem(false);
mLvFavorites.setEmptyView(mSearchTips);
mSearchProgress.setIndeterminate(true);
mMyAdapter.swipResult(null);
mService.startScanAsync();
} else {
boolean isScan = mService.isScanning();
if (isScan) {
mMyAdapter.swipResult(null);
mLvFavorites.setEmptyView(mSearchTips);
mSearchProgress.setIndeterminate(true);
} else {
// TODO it's on UI thread, change to sub thread
mMyAdapter.swipResult(getData());
}
refreshMenuItem(!isScan);
}
}
/**
* When unbind service will call this method
*
* @param className The component name
*/
@Override
public void onServiceDisconnected(ComponentName className) {
}
};
/**
* check gps is open or not
*
* @return true is open
*/
private boolean isGpsOpen() {
return mLocationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);
}
}