| /* |
| * 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.Activity; |
| import android.app.FragmentManager; |
| import android.content.ActivityNotFoundException; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.ServiceConnection; |
| import android.content.res.Resources; |
| import android.database.Cursor; |
| import android.media.AudioManager; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Message; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.Menu; |
| import android.view.MenuInflater; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.ViewConfiguration; |
| import android.view.animation.Animation; |
| import android.view.animation.Animation.AnimationListener; |
| import android.view.animation.AnimationUtils; |
| import android.widget.ImageButton; |
| import android.widget.ImageView; |
| import android.widget.LinearLayout; |
| import android.widget.RelativeLayout; |
| import android.widget.TextView; |
| import android.widget.Toast; |
| import android.widget.Toolbar; |
| |
| import com.android.fmradio.FmStation.Station; |
| import com.android.fmradio.dialogs.FmFavoriteEditDialog; |
| import com.android.fmradio.views.FmScroller; |
| import com.android.fmradio.views.FmSnackBar; |
| import com.android.fmradio.views.FmScroller.EventListener; |
| |
| import java.lang.reflect.Field; |
| |
| /** |
| * This class interact with user, provide FM basic function. |
| */ |
| public class FmMainActivity extends Activity implements FmFavoriteEditDialog.EditFavoriteListener { |
| // Logging |
| private static final String TAG = "FmMainActivity"; |
| |
| // Request code |
| private static final int REQUEST_CODE_FAVORITE = 1; |
| |
| public static final int REQUEST_CODE_RECORDING = 2; |
| |
| // Extra for result of request REQUEST_CODE_RECORDING |
| public static final String EXTRA_RESULT_STRING = "result_string"; |
| |
| // FM |
| private static final String FM = "FM"; |
| |
| // UI views |
| private TextView mTextStationName = null; |
| |
| private TextView mTextStationValue = null; |
| |
| // RDS text view |
| private TextView mTextRds = null; |
| |
| private TextView mActionBarTitle = null; |
| |
| private TextView mNoEarPhoneTxt = null; |
| |
| private ImageButton mButtonDecrease = null; |
| |
| private ImageButton mButtonPrevStation = null; |
| |
| private ImageButton mButtonNextStation = null; |
| |
| private ImageButton mButtonIncrease = null; |
| |
| private ImageButton mButtonAddToFavorite = null; |
| |
| private ImageButton mButtonPlay = null; |
| |
| private ImageView mNoHeadsetImgView = null; |
| |
| private View mNoHeadsetImgViewWrap = null; |
| |
| private float mMiddleShadowSize; |
| |
| private LinearLayout mMainLayout = null; |
| |
| private RelativeLayout mNoHeadsetLayout = null; |
| |
| private LinearLayout mNoEarphoneTextLayout = null; |
| |
| private LinearLayout mBtnPlayInnerContainer = null; |
| |
| private LinearLayout mBtnPlayContainer = null; |
| |
| // Menu items |
| private MenuItem mMenuItemStationlList = null; |
| |
| private MenuItem mMenuItemHeadset = null; |
| |
| private MenuItem mMenuItemStartRecord = null; |
| |
| private MenuItem mMenuItemRecordList = null; |
| |
| // State variables |
| private boolean mIsServiceStarted = false; |
| |
| private boolean mIsServiceBinded = false; |
| |
| private boolean mIsTune = false; |
| |
| private boolean mIsDisablePowerMenu = false; |
| |
| private boolean mIsActivityForeground = true; |
| |
| private int mCurrentStation = FmUtils.DEFAULT_STATION; |
| |
| // Instance variables |
| private FmService mService = null; |
| |
| private Context mContext = null; |
| |
| private Toast mToast = null; |
| |
| private FragmentManager mFragmentManager = null; |
| |
| private AudioManager mAudioManager = null; |
| |
| private FmScroller mScroller; |
| |
| private FmScroller.EventListener mEventListener; |
| |
| // 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); |
| } |
| }; |
| |
| // Button click listeners on UI |
| private final View.OnClickListener mButtonClickListener = new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| switch (v.getId()) { |
| |
| case R.id.button_add_to_favorite: |
| updateFavoriteStation(); |
| break; |
| |
| case R.id.button_decrease: |
| tuneStation(FmUtils.computeDecreaseStation(mCurrentStation)); |
| break; |
| |
| case R.id.button_increase: |
| tuneStation(FmUtils.computeIncreaseStation(mCurrentStation)); |
| break; |
| |
| case R.id.button_prevstation: |
| seekStation(mCurrentStation, false); // false: previous station |
| break; |
| |
| case R.id.button_nextstation: |
| seekStation(mCurrentStation, true); // true: previous station |
| break; |
| |
| case R.id.play_button: |
| if (mService.getPowerStatus() == FmService.POWER_UP) { |
| powerDownFm(); |
| } else { |
| powerUpFm(); |
| } |
| break; |
| default: |
| Log.d(TAG, "mButtonClickListener.onClick, invalid view id"); |
| break; |
| } |
| } |
| }; |
| |
| /** |
| * Main thread handler to update UI |
| */ |
| private Handler mHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| Log.d(TAG, |
| "mHandler.handleMessage, what = " + msg.what + ",hashcode:" |
| + mHandler.hashCode()); |
| Bundle bundle; |
| switch (msg.what) { |
| |
| case FmListener.MSGID_POWERUP_FINISHED: |
| bundle = msg.getData(); |
| boolean isPowerup = (mService.getPowerStatus() == FmService.POWER_UP); |
| int station = bundle.getInt(FmListener.KEY_TUNE_TO_STATION); |
| mCurrentStation = station; |
| refreshStationUI(station); |
| if (isPowerup) { |
| refreshImageButton(true); |
| refreshPopupMenuItem(true); |
| refreshActionMenuItem(true); |
| } else { |
| showToast(getString(R.string.not_available)); |
| } |
| // if not powerup success, refresh power to enable. |
| refreshPlayButton(true); |
| break; |
| |
| case FmListener.MSGID_SWITCH_ANTENNA: |
| bundle = msg.getData(); |
| boolean hasAntenna = bundle.getBoolean(FmListener.KEY_IS_SWITCH_ANTENNA); |
| // if receive headset plug out, need set headset mode on ui |
| if (hasAntenna) { |
| if (mIsActivityForeground) { |
| cancelNoHeadsetAnimation(); |
| playMainAnimation(); |
| } else { |
| changeToMainLayout(); |
| } |
| } else { |
| mMenuItemHeadset.setIcon(R.drawable.btn_fm_headset_selector); |
| if (mIsActivityForeground) { |
| cancelMainAnimation(); |
| playNoHeadsetAnimation(); |
| } else { |
| changeToNoHeadsetLayout(); |
| } |
| } |
| break; |
| |
| case FmListener.MSGID_POWERDOWN_FINISHED: |
| bundle = msg.getData(); |
| refreshImageButton(false); |
| refreshActionMenuItem(false); |
| refreshPopupMenuItem(false); |
| refreshPlayButton(true); |
| break; |
| |
| case FmListener.MSGID_TUNE_FINISHED: |
| bundle = msg.getData(); |
| boolean isTune = bundle.getBoolean(FmListener.KEY_IS_TUNE); |
| boolean isPowerUp = (mService.getPowerStatus() == FmService.POWER_UP); |
| |
| // tune finished, should make power enable |
| mIsDisablePowerMenu = false; |
| float frequency = bundle.getFloat(FmListener.KEY_TUNE_TO_STATION); |
| mCurrentStation = FmUtils.computeStation(frequency); |
| // After tune to station finished, refresh favorite button and |
| // other button status. |
| refreshStationUI(mCurrentStation); |
| // tune fail,should resume button status |
| if (!isTune) { |
| Log.d(TAG, "mHandler.tune: " + isTune); |
| refreshActionMenuItem(isPowerUp); |
| refreshImageButton(isPowerUp); |
| refreshPopupMenuItem(isPowerUp); |
| refreshPlayButton(true); |
| return; |
| } |
| refreshImageButton(true); |
| refreshActionMenuItem(true); |
| refreshPopupMenuItem(true); |
| refreshPlayButton(true); |
| break; |
| |
| case FmListener.MSGID_FM_EXIT: |
| finish(); |
| break; |
| |
| case FmListener.LISTEN_RDSSTATION_CHANGED: |
| bundle = msg.getData(); |
| int rdsStation = bundle.getInt(FmListener.KEY_RDS_STATION); |
| refreshStationUI(rdsStation); |
| break; |
| |
| case FmListener.LISTEN_PS_CHANGED: |
| String stationName = FmStation.getStationName(mContext, mCurrentStation); |
| mTextStationName.setText(stationName); |
| mScroller.notifyAdatperChange(); |
| break; |
| |
| case FmListener.LISTEN_RT_CHANGED: |
| bundle = msg.getData(); |
| String rtString = bundle.getString(FmListener.KEY_RT_INFO); |
| mTextRds.setText(rtString); |
| break; |
| |
| case FmListener.LISTEN_SPEAKER_MODE_CHANGED: |
| bundle = msg.getData(); |
| boolean isSpeakerMode = bundle.getBoolean(FmListener.KEY_IS_SPEAKER_MODE); |
| break; |
| |
| case FmListener.LISTEN_RECORDSTATE_CHANGED: |
| if (mService != null) { |
| mService.updatePlayingNotification(); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| }; |
| |
| // 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(); |
| } |
| if (!mService.isServiceInited()) { |
| mService.initService(mCurrentStation); |
| powerUpFm(); |
| } else { |
| if (mService.isDeviceOpen()) { |
| // tune to station during changing language,we need to tune |
| // again when service bind success |
| if (mIsTune) { |
| tuneStation(mCurrentStation); |
| mIsTune = false; |
| } |
| updateCurrentStation(); |
| updateMenuStatus(); |
| } else { |
| // Normal case will not come here |
| // Need to exit FM for this case |
| exitService(); |
| finish(); |
| } |
| } |
| } |
| |
| /** |
| * When unbind service will call this method |
| * |
| * @param className The component name |
| */ |
| @Override |
| public void onServiceDisconnected(ComponentName className) { |
| } |
| }; |
| |
| private class NoHeadsetAlpaOutListener implements AnimationListener { |
| |
| @Override |
| public void onAnimationEnd(Animation animation) { |
| if (!isAntennaAvailable()) { |
| return; |
| } |
| changeToMainLayout(); |
| cancelMainAnimation(); |
| Animation anim = AnimationUtils.loadAnimation(mContext, |
| R.anim.main_alpha_in); |
| mMainLayout.startAnimation(anim); |
| anim = AnimationUtils.loadAnimation(mContext, R.anim.floatbtn_alpha_in); |
| |
| mBtnPlayContainer.startAnimation(anim); |
| } |
| |
| @Override |
| public void onAnimationRepeat(Animation animation) { |
| } |
| |
| @Override |
| public void onAnimationStart(Animation animation) { |
| mNoHeadsetImgViewWrap.setElevation(0); |
| } |
| } |
| |
| private class NoHeadsetAlpaInListener implements AnimationListener { |
| |
| @Override |
| public void onAnimationEnd(Animation animation) { |
| if (isAntennaAvailable()) { |
| return; |
| } |
| changeToNoHeadsetLayout(); |
| cancelNoHeadsetAnimation(); |
| Animation anim = AnimationUtils.loadAnimation(mContext, |
| R.anim.noeaphone_alpha_in); |
| mNoHeadsetLayout.startAnimation(anim); |
| } |
| |
| @Override |
| public void onAnimationRepeat(Animation animation) { |
| } |
| |
| @Override |
| public void onAnimationStart(Animation animation) { |
| mNoHeadsetImgViewWrap.setElevation(mMiddleShadowSize); |
| } |
| |
| } |
| |
| /** |
| * Update the favorite UI state |
| */ |
| private void updateFavoriteStation() { |
| // Judge the current output and switch between the devices. |
| if (FmStation.isFavoriteStation(mContext, mCurrentStation)) { |
| FmStation.removeFromFavorite(mContext, mCurrentStation); |
| mButtonAddToFavorite.setImageResource(R.drawable.btn_fm_favorite_off_selector); |
| // Notify scroller |
| mScroller.onRemoveFavorite(); |
| mTextStationName.setText(FmStation.getStationName(mContext, mCurrentStation)); |
| } else { |
| // Add the station to favorite |
| if (FmStation.isStationExist(mContext, mCurrentStation)) { |
| FmStation.addToFavorite(mContext, mCurrentStation); |
| } else { |
| ContentValues values = new ContentValues(2); |
| values.put(Station.FREQUENCY, mCurrentStation); |
| values.put(Station.IS_FAVORITE, true); |
| FmStation.insertStationToDb(mContext, values); |
| } |
| mButtonAddToFavorite.setImageResource(R.drawable.btn_fm_favorite_on_selector); |
| // Notify scroller |
| mScroller.onAddFavorite(); |
| } |
| } |
| |
| /** |
| * Called when the activity is first created, initial variables |
| * |
| * @param savedInstanceState The saved bundle in onSaveInstanceState |
| */ |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| // Bind the activity to FM audio stream. |
| setVolumeControlStream(AudioManager.STREAM_MUSIC); |
| setContentView(R.layout.main); |
| try { |
| ViewConfiguration config = ViewConfiguration.get(this); |
| Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); |
| if (menuKeyField != null) { |
| menuKeyField.setAccessible(true); |
| menuKeyField.setBoolean(config, false); |
| } |
| } catch (NoSuchFieldException e) { |
| e.printStackTrace(); |
| } catch (IllegalAccessException e) { |
| e.printStackTrace(); |
| } catch (IllegalArgumentException e) { |
| e.printStackTrace(); |
| } |
| |
| mFragmentManager = getFragmentManager(); |
| mContext = getApplicationContext(); |
| |
| initUiComponent(); |
| registerButtonClickListener(); |
| mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
| |
| mScroller = (FmScroller) findViewById(R.id.multiscroller); |
| mScroller.initialize(); |
| mEventListener = new EventListener() { |
| @Override |
| public void onRename(int frequency) { |
| showRenameDialog(frequency); |
| } |
| |
| @Override |
| public void onRemoveFavorite(int frequency) { |
| // TODO it's on UI thread, change to sub thread |
| if (FmStation.isFavoriteStation(mContext, frequency)) { |
| FmStation.removeFromFavorite(mContext, frequency); |
| if (mCurrentStation == frequency) { |
| mTextStationName.setText(FmStation.getStationName(mContext, frequency)); |
| } |
| mButtonAddToFavorite.setImageResource(R.drawable.btn_fm_favorite_off_selector); |
| // Notify scroller |
| mScroller.onRemoveFavorite(); |
| } |
| } |
| |
| @Override |
| public void onPlay(int frequency) { |
| if (frequency != 0 && (mService.getPowerStatus() == FmService.POWER_UP)) { |
| tuneStation(frequency); |
| } |
| } |
| }; |
| mScroller.registerListener(mEventListener); |
| } |
| |
| @Override |
| public void editFavorite(int stationFreq, String name) { |
| FmStation.updateStationToDb(mContext, stationFreq, name); |
| if (mCurrentStation == stationFreq) { |
| String stationName = FmStation.getStationName(mContext, mCurrentStation); |
| mTextStationName.setText(stationName); |
| } |
| mScroller.notifyAdatperChange(); |
| String title = getString(R.string.toast_station_renamed); |
| FmSnackBar.make(FmMainActivity.this, title, null, null, |
| FmSnackBar.DEFAULT_DURATION).show(); |
| } |
| |
| /** |
| * Display the rename dialog |
| * |
| * @param frequency The display frequency |
| */ |
| public void showRenameDialog(int frequency) { |
| if (mService != null) { |
| String name = FmStation.getStationName(mContext, frequency); |
| FmFavoriteEditDialog newFragment = FmFavoriteEditDialog.newInstance(name, frequency); |
| newFragment.show(mFragmentManager, "TAG_EDIT_FAVORITE"); |
| mFragmentManager.executePendingTransactions(); |
| } |
| } |
| |
| /** |
| * Go to station list activity |
| */ |
| private void enterStationList() { |
| if (mService != null) { |
| // AMS change the design for background start |
| // activity. need check app is background in app code |
| if (mService.isActivityForeground()) { |
| Intent intent = new Intent(); |
| intent.setClass(FmMainActivity.this, FmFavoriteActivity.class); |
| startActivityForResult(intent, REQUEST_CODE_FAVORITE); |
| } |
| } |
| } |
| |
| /** |
| * Refresh the favorite button with the given station, if the station |
| * is favorite station, show favorite icon, else show non-favorite icon. |
| * |
| * @param station The station frequency |
| */ |
| private void refreshStationUI(int station) { |
| if (FmUtils.isFirstTimePlayFm(mContext)) { |
| Log.d(TAG, "refreshStationUI, set station value null when it is first time "); |
| return; |
| } |
| // TODO it's on UI thread, change to sub thread |
| // Change the station frequency displayed. |
| mTextStationValue.setText(FmUtils.formatStation(station)); |
| // Show or hide the favorite icon |
| if (FmStation.isFavoriteStation(mContext, station)) { |
| mButtonAddToFavorite.setImageResource(R.drawable.btn_fm_favorite_on_selector); |
| } else { |
| mButtonAddToFavorite.setImageResource(R.drawable.btn_fm_favorite_off_selector); |
| } |
| |
| String stationName = ""; |
| String radioText = ""; |
| ContentResolver resolver = mContext.getContentResolver(); |
| Cursor cursor = null; |
| try { |
| cursor = resolver.query( |
| Station.CONTENT_URI, |
| FmStation.COLUMNS, |
| Station.FREQUENCY + "=?", |
| new String[] { String.valueOf(mCurrentStation) }, |
| null); |
| if (cursor != null && cursor.moveToFirst()) { |
| // If the station name is not exist, show program service(PS) instead |
| stationName = cursor.getString(cursor.getColumnIndex(Station.STATION_NAME)); |
| if (TextUtils.isEmpty(stationName)) { |
| stationName = cursor.getString(cursor.getColumnIndex(Station.PROGRAM_SERVICE)); |
| } |
| radioText = cursor.getString(cursor.getColumnIndex(Station.RADIO_TEXT)); |
| |
| } else { |
| Log.d(TAG, "showPlayingNotification, cursor is null"); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| mTextStationName.setText(stationName); |
| mTextRds.setText(radioText); |
| } |
| |
| /** |
| * Start and bind service, reduction variable values if configuration changed |
| */ |
| @Override |
| public void onStart() { |
| super.onStart(); |
| // check layout onstart |
| if (isAntennaAvailable()) { |
| changeToMainLayout(); |
| } else { |
| changeToNoHeadsetLayout(); |
| } |
| |
| // Should start FM service first. |
| if (null == startService(new Intent(FmMainActivity.this, FmService.class))) { |
| Log.e(TAG, "onStart, cannot start FM service"); |
| return; |
| } |
| |
| mIsServiceStarted = true; |
| mIsServiceBinded = bindService(new Intent(FmMainActivity.this, FmService.class), |
| mServiceConnection, Context.BIND_AUTO_CREATE); |
| |
| if (!mIsServiceBinded) { |
| Log.e(TAG, "onStart, cannot bind FM service"); |
| finish(); |
| return; |
| } |
| } |
| |
| /** |
| * Refresh UI, when stop search, dismiss search dialog, |
| * pop up recording dialog if FM stopped when recording in |
| * background |
| */ |
| @Override |
| public void onResume() { |
| super.onResume(); |
| mIsActivityForeground = true; |
| mScroller.onResume(); |
| if (null == mService) { |
| Log.d(TAG, "onResume, mService is null"); |
| return; |
| } |
| mService.setFmMainActivityForeground(mIsActivityForeground); |
| if (FmRecorder.STATE_RECORDING != mService.getRecorderState()) { |
| mService.removeNotification(); |
| } |
| updateMenuStatus(); |
| } |
| |
| /** |
| * When activity is paused call this method, indicate activity |
| * enter background if press exit, power down FM |
| */ |
| @Override |
| public void onPause() { |
| mIsActivityForeground = false; |
| if (null != mService) { |
| mService.setFmMainActivityForeground(mIsActivityForeground); |
| } |
| mScroller.onPause(); |
| super.onPause(); |
| } |
| |
| /** |
| * Called when activity enter stopped state, |
| * unbind service, if exit pressed, stop service |
| */ |
| @Override |
| public void onStop() { |
| if (null != mService) { |
| mService.setNotificationClsName(FmMainActivity.class.getName()); |
| mService.updatePlayingNotification(); |
| } |
| if (mIsServiceBinded) { |
| unbindService(mServiceConnection); |
| mIsServiceBinded = false; |
| } |
| super.onStop(); |
| } |
| |
| /** |
| * W activity destroy, unregister broadcast receiver and remove handler message |
| */ |
| @Override |
| public void onDestroy() { |
| // need to call this function because if doesn't do this,after |
| // configuration change will have many instance and recording time |
| // or playing time will not refresh |
| // Remove all the handle message |
| mHandler.removeCallbacksAndMessages(null); |
| if (mService != null) { |
| mService.unregisterFmRadioListener(mFmRadioListener); |
| } |
| mFmRadioListener = null; |
| mScroller.closeAdapterCursor(); |
| mScroller.unregisterListener(mEventListener); |
| super.onDestroy(); |
| } |
| |
| /** |
| * Create options menu |
| * |
| * @param menu The option menu |
| * @return true or false indicate need to handle other menu item |
| */ |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| MenuInflater inflater = getMenuInflater(); |
| inflater.inflate(R.menu.fm_action_bar, menu); |
| mMenuItemStationlList = menu.findItem(R.id.fm_station_list); |
| mMenuItemHeadset = menu.findItem(R.id.fm_headset); |
| mMenuItemStartRecord = menu.findItem(R.id.fm_start_record); |
| mMenuItemRecordList = menu.findItem(R.id.fm_record_list); |
| return true; |
| } |
| |
| /** |
| * Prepare options menu |
| * |
| * @param menu The option menu |
| * @return true or false indicate need to handle other menu item |
| */ |
| @Override |
| public boolean onPrepareOptionsMenu(Menu menu) { |
| if (null == mService) { |
| Log.d(TAG, "onPrepareOptionsMenu, mService is null"); |
| return true; |
| } |
| int powerStatus = mService.getPowerStatus(); |
| boolean isPowerUp = (powerStatus == FmService.POWER_UP); |
| boolean isPowerdown = (powerStatus == FmService.POWER_DOWN); |
| boolean isSeeking = mService.isSeeking(); |
| boolean isSpeakerUsed = mService.isSpeakerUsed(); |
| // if fm power down by other app, should enable power menu, make it to |
| // powerup. |
| refreshActionMenuItem(isSeeking ? false : isPowerUp); |
| refreshPlayButton(isSeeking ? false |
| : (isPowerUp || (isPowerdown && !mIsDisablePowerMenu))); |
| mMenuItemHeadset.setIcon(isSpeakerUsed ? R.drawable.btn_fm_speaker_selector |
| : R.drawable.btn_fm_headset_selector); |
| return true; |
| } |
| |
| /** |
| * Handle event when option item selected |
| * |
| * @param item The clicked item |
| * @return true or false indicate need to handle other menu item or not |
| */ |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| switch (item.getItemId()) { |
| case android.R.id.home: |
| onBackPressed(); |
| break; |
| |
| case R.id.fm_station_list: |
| refreshImageButton(false); |
| refreshActionMenuItem(false); |
| refreshPopupMenuItem(false); |
| refreshPlayButton(false); |
| // Show favorite activity. |
| enterStationList(); |
| break; |
| |
| case R.id.earphone_menu: |
| setSpeakerPhoneOn(false); |
| mMenuItemHeadset.setIcon(R.drawable.btn_fm_headset_selector); |
| invalidateOptionsMenu(); |
| break; |
| |
| case R.id.speaker_menu: |
| setSpeakerPhoneOn(true); |
| mMenuItemHeadset.setIcon(R.drawable.btn_fm_speaker_selector); |
| invalidateOptionsMenu(); |
| break; |
| |
| case R.id.fm_start_record: |
| Intent recordIntent = new Intent(this, FmRecordActivity.class); |
| recordIntent.putExtra(FmStation.CURRENT_STATION, mCurrentStation); |
| startActivityForResult(recordIntent, REQUEST_CODE_RECORDING); |
| break; |
| |
| case R.id.fm_record_list: |
| Intent playMusicIntent = new Intent(Intent.ACTION_VIEW); |
| int playlistId = FmRecorder.getPlaylistId(mContext); |
| Bundle extras = new Bundle(); |
| extras.putInt("playlist", playlistId); |
| try { |
| playMusicIntent.putExtras(extras); |
| playMusicIntent.setClassName("com.google.android.music", |
| "com.google.android.music.ui.TrackContainerActivity"); |
| playMusicIntent.setType("vnd.android.cursor.dir/playlist"); |
| startActivity(playMusicIntent); |
| } catch (ActivityNotFoundException e1) { |
| try { |
| playMusicIntent = new Intent(Intent.ACTION_VIEW); |
| playMusicIntent.putExtras(extras); |
| playMusicIntent.setType("vnd.android.cursor.dir/playlist"); |
| startActivity(playMusicIntent); |
| } catch (ActivityNotFoundException e2) { |
| // No activity respond |
| Log.d(TAG, |
| "onOptionsItemSelected, No activity respond playlist view intent"); |
| } |
| } |
| break; |
| default: |
| Log.e(TAG, "onOptionsItemSelected, invalid options menu item."); |
| break; |
| } |
| return super.onOptionsItemSelected(item); |
| } |
| |
| /** |
| * Check whether antenna is available |
| * |
| * @return true or false indicate antenna available or not |
| */ |
| private boolean isAntennaAvailable() { |
| return mAudioManager.isWiredHeadsetOn(); |
| } |
| |
| /** |
| * When on activity result, tune to station which is from station list |
| * |
| * @param requestCode The request code |
| * @param resultCode The result code |
| * @param data The intent from station list |
| */ |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
| if (RESULT_OK == resultCode) { |
| if (REQUEST_CODE_RECORDING == requestCode) { |
| final Uri playUri = data.getData(); |
| boolean isSaved = playUri != null; |
| String title = data.getStringExtra(EXTRA_RESULT_STRING); |
| String action = null; |
| FmSnackBar.OnActionTriggerListener listener = null; |
| |
| if (isSaved) { |
| action = FmMainActivity.this.getString(R.string.toast_listen); |
| listener = new FmSnackBar.OnActionTriggerListener() { |
| @Override |
| public void onActionTriggered() { |
| Intent playMusicIntent = new Intent(Intent.ACTION_VIEW); |
| try { |
| playMusicIntent.setClassName("com.google.android.music", |
| "com.google.android.music.AudioPreview"); |
| playMusicIntent.setDataAndType(playUri, "audio/3gpp"); |
| startActivity(playMusicIntent); |
| } catch (ActivityNotFoundException e1) { |
| try { |
| playMusicIntent = new Intent(Intent.ACTION_VIEW); |
| playMusicIntent.setDataAndType(playUri, "audio/3gpp"); |
| startActivity(playMusicIntent); |
| } catch (ActivityNotFoundException e2) { |
| // No activity respond |
| Log.d(TAG,"onActivityResult, no activity " |
| + "respond play record file intent"); |
| } |
| } |
| } |
| }; |
| } |
| FmSnackBar.make(FmMainActivity.this, title, action, listener, |
| FmSnackBar.DEFAULT_DURATION).show(); |
| } else if (REQUEST_CODE_FAVORITE == requestCode) { |
| int iStation = |
| data.getIntExtra(FmFavoriteActivity.ACTIVITY_RESULT, mCurrentStation); |
| // Tune to this station. |
| mCurrentStation = iStation; |
| // if tune from station list, we should disable power menu, |
| // especially for power down state |
| mIsDisablePowerMenu = true; |
| Log.d(TAG, "onActivityForReult:" + mIsDisablePowerMenu); |
| if (null == mService) { |
| Log.d(TAG, "onActivityResult, mService is null"); |
| mIsTune = true; |
| return; |
| } |
| tuneStation(iStation); |
| } else { |
| Log.e(TAG, "onActivityResult, invalid requestcode."); |
| return; |
| } |
| } |
| |
| // TODO it's on UI thread, change to sub thread |
| if (FmStation.isFavoriteStation(mContext, mCurrentStation)) { |
| mButtonAddToFavorite.setImageResource(R.drawable.btn_fm_favorite_on_selector); |
| } else { |
| mButtonAddToFavorite.setImageResource(R.drawable.btn_fm_favorite_off_selector); |
| } |
| mTextStationName.setText(FmStation.getStationName(mContext, mCurrentStation)); |
| } |
| |
| /** |
| * Power up FM |
| */ |
| private void powerUpFm() { |
| refreshImageButton(false); |
| refreshActionMenuItem(false); |
| refreshPopupMenuItem(false); |
| refreshPlayButton(false); |
| mService.powerUpAsync(FmUtils.computeFrequency(mCurrentStation)); |
| } |
| |
| /** |
| * Power down FM |
| */ |
| private void powerDownFm() { |
| refreshImageButton(false); |
| refreshActionMenuItem(false); |
| refreshPopupMenuItem(false); |
| refreshPlayButton(false); |
| mService.powerDownAsync(); |
| } |
| |
| private void setSpeakerPhoneOn(boolean isSpeaker) { |
| if (isSpeaker) { |
| mService.setSpeakerPhoneOn(true); |
| } else { |
| mService.setSpeakerPhoneOn(false); |
| } |
| } |
| |
| /** |
| * Tune a station |
| * |
| * @param station The tune station |
| */ |
| private void tuneStation(final int station) { |
| refreshImageButton(false); |
| refreshActionMenuItem(false); |
| refreshPopupMenuItem(false); |
| refreshPlayButton(false); |
| mService.tuneStationAsync(FmUtils.computeFrequency(station)); |
| } |
| |
| /** |
| * Seek station according current frequency and direction |
| * |
| * @param station The seek start station |
| * @param direction The seek direction |
| */ |
| private void seekStation(final int station, boolean direction) { |
| // If the seek AsyncTask has been executed and not canceled, cancel it |
| // before start new. |
| refreshImageButton(false); |
| refreshActionMenuItem(false); |
| refreshPopupMenuItem(false); |
| refreshPlayButton(false); |
| mService.seekStationAsync(FmUtils.computeFrequency(station), direction); |
| } |
| |
| private void refreshImageButton(boolean enabled) { |
| mButtonDecrease.setEnabled(enabled); |
| mButtonPrevStation.setEnabled(enabled); |
| mButtonNextStation.setEnabled(enabled); |
| mButtonIncrease.setEnabled(enabled); |
| mButtonAddToFavorite.setEnabled(enabled); |
| } |
| |
| // Refresh action menu except power menu |
| private void refreshActionMenuItem(boolean enabled) { |
| // action menu |
| if (null != mMenuItemStationlList) { |
| // if power down by other app, should disable station list, over |
| // menu |
| mMenuItemStationlList.setEnabled(enabled); |
| // If BT headset is in use, need to disable speaker/earphone switching menu. |
| mMenuItemHeadset.setEnabled(enabled && !mService.isBluetoothHeadsetInUse()); |
| } |
| } |
| |
| // Refresh play/stop float button |
| private void refreshPlayButton(boolean enabled) { |
| // action menu |
| boolean isPowerUp = (mService.getPowerStatus() == FmService.POWER_UP); |
| mButtonPlay.setEnabled(enabled); |
| mButtonPlay.setImageResource((isPowerUp |
| ? R.drawable.btn_fm_stop_selector |
| : R.drawable.btn_fm_start_selector)); |
| Resources r = getResources(); |
| mBtnPlayInnerContainer.setBackground(r.getDrawable(R.drawable.fb_red)); |
| mScroller.refreshPlayIndicator(mCurrentStation, isPowerUp); |
| } |
| |
| private void refreshPopupMenuItem(boolean enabled) { |
| if (null != mMenuItemStationlList) { |
| mMenuItemStartRecord.setEnabled(enabled); |
| } |
| } |
| |
| /** |
| * Called when back pressed |
| */ |
| @Override |
| public void onBackPressed() { |
| // exit fm, disable all button |
| if ((null != mService) && (mService.getPowerStatus() == FmService.POWER_DOWN)) { |
| refreshImageButton(false); |
| refreshActionMenuItem(false); |
| refreshPopupMenuItem(false); |
| refreshPlayButton(false); |
| exitService(); |
| return; |
| } |
| super.onBackPressed(); |
| } |
| |
| private void showToast(CharSequence text) { |
| if (null == mToast) { |
| mToast = Toast.makeText(mContext, text, Toast.LENGTH_SHORT); |
| } |
| mToast.setText(text); |
| mToast.show(); |
| } |
| |
| @Override |
| protected void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| } |
| |
| /** |
| * Exit FM service |
| */ |
| private void exitService() { |
| if (mIsServiceBinded) { |
| unbindService(mServiceConnection); |
| mIsServiceBinded = false; |
| } |
| |
| if (mIsServiceStarted) { |
| stopService(new Intent(FmMainActivity.this, FmService.class)); |
| mIsServiceStarted = false; |
| } |
| } |
| |
| /** |
| * Update current station according service station |
| */ |
| private void updateCurrentStation() { |
| // get the frequency from service, set frequency in activity, UI, |
| // database |
| // same as the frequency in service |
| int freq = mService.getFrequency(); |
| if (FmUtils.isValidStation(freq)) { |
| if (mCurrentStation != freq) { |
| mCurrentStation = freq; |
| FmStation.setCurrentStation(mContext, mCurrentStation); |
| refreshStationUI(mCurrentStation); |
| } |
| } |
| } |
| |
| /** |
| * Update menu status, and animation |
| */ |
| private void updateMenuStatus() { |
| int powerStatus = mService.getPowerStatus(); |
| boolean isPowerUp = (powerStatus == FmService.POWER_UP); |
| boolean isDuringPowerup = (powerStatus == FmService.DURING_POWER_UP); |
| boolean isSeeking = mService.isSeeking(); |
| boolean isPowerdown = (powerStatus == FmService.POWER_DOWN); |
| boolean isSpeakerUsed = mService.isSpeakerUsed(); |
| boolean fmStatus = (isSeeking || isDuringPowerup); |
| // when seeking, all button should disabled, |
| // else should update as origin status |
| refreshImageButton(fmStatus ? false : isPowerUp); |
| refreshPopupMenuItem(fmStatus ? false : isPowerUp); |
| refreshActionMenuItem(fmStatus ? false : isPowerUp); |
| // if fm power down by other app, should enable power button |
| // to powerup. |
| Log.d(TAG, "updateMenuStatus.mIsDisablePowerMenu: " + mIsDisablePowerMenu); |
| refreshPlayButton(fmStatus ? false |
| : (isPowerUp || (isPowerdown && !mIsDisablePowerMenu))); |
| if (null != mMenuItemHeadset) { |
| mMenuItemHeadset.setIcon(isSpeakerUsed ? R.drawable.btn_fm_speaker_selector |
| : R.drawable.btn_fm_headset_selector); |
| } |
| |
| } |
| |
| private void initUiComponent() { |
| mTextRds = (TextView) findViewById(R.id.station_rds); |
| mTextStationValue = (TextView) findViewById(R.id.station_value); |
| mButtonAddToFavorite = (ImageButton) findViewById(R.id.button_add_to_favorite); |
| mTextStationName = (TextView) findViewById(R.id.station_name); |
| mButtonDecrease = (ImageButton) findViewById(R.id.button_decrease); |
| mButtonIncrease = (ImageButton) findViewById(R.id.button_increase); |
| mButtonPrevStation = (ImageButton) findViewById(R.id.button_prevstation); |
| mButtonNextStation = (ImageButton) findViewById(R.id.button_nextstation); |
| |
| // put favorite button here since it might be used very early in |
| // changing recording mode |
| mCurrentStation = FmStation.getCurrentStation(mContext); |
| refreshStationUI(mCurrentStation); |
| |
| // l new |
| mMainLayout = (LinearLayout) findViewById(R.id.main_view); |
| mNoHeadsetLayout = (RelativeLayout) findViewById(R.id.no_headset); |
| mNoEarphoneTextLayout = (LinearLayout) findViewById(R.id.no_bottom); |
| mBtnPlayContainer = (LinearLayout) findViewById(R.id.play_button_container); |
| mBtnPlayInnerContainer = (LinearLayout) findViewById(R.id.play_button_inner_container); |
| mButtonPlay = (ImageButton) findViewById(R.id.play_button); |
| mNoEarPhoneTxt = (TextView) findViewById(R.id.no_eaphone_text); |
| mNoHeadsetImgView = (ImageView) findViewById(R.id.no_headset_img); |
| mNoHeadsetImgViewWrap = findViewById(R.id.no_middle); |
| mMiddleShadowSize = getResources().getDimension(R.dimen.fm_middle_shadow); |
| // main ui layout params |
| final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); |
| setActionBar(toolbar); |
| getActionBar().setTitle(""); |
| } |
| |
| private void registerButtonClickListener() { |
| mButtonAddToFavorite.setOnClickListener(mButtonClickListener); |
| mButtonDecrease.setOnClickListener(mButtonClickListener); |
| mButtonIncrease.setOnClickListener(mButtonClickListener); |
| mButtonPrevStation.setOnClickListener(mButtonClickListener); |
| mButtonNextStation.setOnClickListener(mButtonClickListener); |
| mButtonPlay.setOnClickListener(mButtonClickListener); |
| } |
| |
| /** |
| * play main animation |
| */ |
| private void playMainAnimation() { |
| if (null == mService) { |
| Log.e(TAG, "playMainAnimation, mService is null"); |
| return; |
| } |
| if (mMainLayout.isShown()) { |
| Log.w(TAG, "playMainAnimation, main layout has already shown"); |
| return; |
| } |
| Animation animation = AnimationUtils.loadAnimation(mContext, |
| R.anim.noeaphone_alpha_out); |
| mNoEarPhoneTxt.startAnimation(animation); |
| mNoHeadsetImgView.startAnimation(animation); |
| |
| animation = AnimationUtils.loadAnimation(mContext, |
| R.anim.noeaphone_translate_out); |
| animation.setAnimationListener(new NoHeadsetAlpaOutListener()); |
| mNoEarphoneTextLayout.startAnimation(animation); |
| } |
| |
| /** |
| * clear main layout animation |
| */ |
| private void cancelMainAnimation() { |
| mNoEarPhoneTxt.clearAnimation(); |
| mNoHeadsetImgView.clearAnimation(); |
| mNoEarphoneTextLayout.clearAnimation(); |
| } |
| |
| /** |
| * play change to no headset layout animation |
| */ |
| private void playNoHeadsetAnimation() { |
| if (null == mService) { |
| Log.e(TAG, "playNoHeadsetAnimation, mService is null"); |
| return; |
| } |
| if (mNoHeadsetLayout.isShown()) { |
| Log.w(TAG,"playNoHeadsetAnimation, no headset layout has already shown"); |
| return; |
| } |
| Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.main_alpha_out); |
| mMainLayout.startAnimation(animation); |
| animation.setAnimationListener(new NoHeadsetAlpaInListener()); |
| mBtnPlayContainer.startAnimation(animation); |
| } |
| |
| /** |
| * clear no headset layout animation |
| */ |
| private void cancelNoHeadsetAnimation() { |
| mMainLayout.clearAnimation(); |
| mBtnPlayContainer.clearAnimation(); |
| } |
| |
| /** |
| * change to main layout |
| */ |
| private void changeToMainLayout() { |
| mNoEarphoneTextLayout.setVisibility(View.GONE); |
| mNoHeadsetImgView.setVisibility(View.GONE); |
| mNoHeadsetImgViewWrap.setVisibility(View.GONE); |
| mNoHeadsetLayout.setVisibility(View.GONE); |
| // change to main layout |
| mMainLayout.setVisibility(View.VISIBLE); |
| mBtnPlayContainer.setVisibility(View.VISIBLE); |
| } |
| |
| /** |
| * change to no headset layout |
| */ |
| private void changeToNoHeadsetLayout() { |
| mMainLayout.setVisibility(View.GONE); |
| mBtnPlayContainer.setVisibility(View.GONE); |
| mNoEarphoneTextLayout.setVisibility(View.VISIBLE); |
| mNoHeadsetImgView.setVisibility(View.VISIBLE); |
| mNoHeadsetImgViewWrap.setVisibility(View.VISIBLE); |
| mNoHeadsetLayout.setVisibility(View.VISIBLE); |
| mNoHeadsetImgViewWrap.setElevation(mMiddleShadowSize); |
| } |
| } |