FmService.java revision 44ce72c72613263d4f2d026263f3ac53cf0cde8a
1a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang/* 2a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Copyright (C) 2014 The Android Open Source Project 3a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 4a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Licensed under the Apache License, Version 2.0 (the "License"); 5a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * you may not use this file except in compliance with the License. 6a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * You may obtain a copy of the License at 7a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 8a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * http://www.apache.org/licenses/LICENSE-2.0 9a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 10a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Unless required by applicable law or agreed to in writing, software 11a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * distributed under the License is distributed on an "AS IS" BASIS, 12a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * See the License for the specific language governing permissions and 14a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * limitations under the License. 15a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 16a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 17a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangpackage com.android.fmradio; 18a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 19a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.ActivityManager; 20a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.Notification; 21a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.Notification.BigTextStyle; 22a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.PendingIntent; 23a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.Service; 24a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.BroadcastReceiver; 25a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.ContentResolver; 26a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.ContentValues; 27a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.Context; 28a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.Intent; 29a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.IntentFilter; 30a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.res.Configuration; 31a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.database.Cursor; 32a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.graphics.Bitmap; 33a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioDevicePort; 34a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioDevicePortConfig; 35a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioFormat; 36a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioGain; 37a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioGainConfig; 38a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioManager; 39a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioManager.OnAudioFocusChangeListener; 40a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioManager.OnAudioPortUpdateListener; 41a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioMixPort; 42a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioPatch; 43a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioPort; 44a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioPortConfig; 45a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioRecord; 46a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioSystem; 47a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioTrack; 48a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.MediaRecorder; 49a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.VolumeProvider; 50a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.session.MediaController; 51a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.session.MediaSession; 52a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.session.MediaSessionManager; 53a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.session.PlaybackState; 54a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.net.Uri; 55a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Binder; 56a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Bundle; 57a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Handler; 58a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.HandlerThread; 59a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.IBinder; 60a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Looper; 61a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Message; 62a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.PowerManager; 63a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.PowerManager.WakeLock; 64a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.ResultReceiver; 65a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.text.TextUtils; 66a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.util.Log; 67a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 68a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport com.android.fmradio.FmStation.Station; 69a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 70a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport java.util.ArrayList; 71a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport java.util.Arrays; 72a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport java.util.HashMap; 73a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport java.util.Iterator; 74a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 75a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang/** 76a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Background service to control FM or do background tasks. 77a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 78a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangpublic class FmService extends Service implements FmRecorder.OnRecorderStateChangedListener { 79a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Logging 80a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final String TAG = "FmService"; 81a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 82a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Broadcast messages from other sounder APP to FM service 83a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final String SOUND_POWER_DOWN_MSG = "com.android.music.musicservicecommand"; 8444ce72c72613263d4f2d026263f3ac53cf0cde8aBenson Huang private static final String FM_SEEK_PREVIOUS = "fmradio.seek.previous"; 8544ce72c72613263d4f2d026263f3ac53cf0cde8aBenson Huang private static final String FM_SEEK_NEXT = "fmradio.seek.next"; 8644ce72c72613263d4f2d026263f3ac53cf0cde8aBenson Huang private static final String FM_TURN_OFF = "fmradio.turnoff"; 87a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final String CMDPAUSE = "pause"; 88a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 89a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // HandlerThread Keys 90a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final String FM_FREQUENCY = "frequency"; 91a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final String OPTION = "option"; 92a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final String RECODING_FILE_NAME = "name"; 93a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 94a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // RDS events 95a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // PS 96a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int RDS_EVENT_PROGRAMNAME = 0x0008; 97a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // RT 98a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int RDS_EVENT_LAST_RADIOTEXT = 0x0040; 99a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // AF 100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int RDS_EVENT_AF = 0x0080; 101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Headset 103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int HEADSET_PLUG_IN = 1; 104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Notification id 106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int NOTIFICATION_ID = 1; 107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Set audio policy for FM 109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // should check AUDIO_POLICY_FORCE_FOR_MEDIA in audio_policy.h 110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int FOR_PROPRIETARY = 1; 111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Forced Use value 112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int mForcedUseForMedia; 113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // FM recorder 115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmRecorder mFmRecorder = null; 116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private BroadcastReceiver mSdcardListener = null; 117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int mRecordState = FmRecorder.STATE_INVALID; 118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int mRecorderErrorType = -1; 119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // If eject record sdcard, should set Value false to not record. 120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Key is sdcard path(like "/storage/sdcard0"), V is to enable record or 121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // not. 122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private HashMap<String, Boolean> mSdcardStateMap = new HashMap<String, Boolean>(); 123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // The show name in save dialog but saved in service 124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // If modify the save title it will be not null, otherwise it will be null 125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private String mModifiedRecordingName = null; 126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // record the listener list, will notify all listener in list 127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private ArrayList<Record> mRecords = new ArrayList<Record>(); 128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // record FM whether in recording mode 129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsInRecordingMode = false; 130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // record sd card path when start recording 131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static String sRecordingSdcard = FmUtils.getDefaultStoragePath(); 132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // RDS 134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // PS String 135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private String mPsString = ""; 136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // RT String 137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private String mRtTextString = ""; 138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Notification target class name 139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private String mTargetClassName = FmMainActivity.class.getName(); 140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // RDS thread use to receive the information send by station 141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private Thread mRdsThread = null; 142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // record whether RDS thread exit 143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsRdsThreadExit = false; 144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // State variables 146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record whether FM is in native scan state 147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsNativeScanning = false; 148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record whether FM is in scan thread 149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsScanning = false; 150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record whether FM is in seeking state 151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsNativeSeeking = false; 152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record whether FM is in native seek 153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsSeeking = false; 154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record whether searching progress is canceled 155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsStopScanCalled = false; 156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record whether is speaker used 157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsSpeakerUsed = false; 158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record whether device is open 159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsDeviceOpen = false; 160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record Power Status 161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int mPowerStatus = POWER_DOWN; 162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static int POWER_UP = 0; 164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static int DURING_POWER_UP = 1; 165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static int POWER_DOWN = 2; 166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Record whether service is init 167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsServiceInited = false; 168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Fm power down by loss audio focus,should make power down menu item can 169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // click 170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsPowerDown = false; 171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // distance is over 100 miles(160934.4m) 172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsDistanceExceed = false; 173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // FmMainActivity foreground 174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsFmMainForeground = true; 175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // FmFavoriteActivity foreground 176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsFmFavoriteForground = false; 177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Instance variables 178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private Context mContext = null; 179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private AudioManager mAudioManager = null; 180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private ActivityManager mActivityManager = null; 181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang //private MediaPlayer mFmPlayer = null; 182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private WakeLock mWakeLock = null; 183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Audio focus is held or not 184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsAudioFocusHeld = false; 185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Focus transient lost 186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mPausedByTransientLossOfFocus = false; 187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int mCurrentStation = FmUtils.DEFAULT_STATION; 188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Headset plug state (0:long antenna plug in, 1:long antenna plug out) 189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int mValueHeadSetPlug = 1; 190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // For bind service 191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private final IBinder mBinder = new ServiceBinder(); 192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Broadcast to receive the external event 193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private FmServiceBroadcastReceiver mBroadcastReceiver = null; 194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Async handler 195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private FmRadioServiceHandler mFmServiceHandler; 196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Lock for lose audio focus and receive SOUND_POWER_DOWN_MSG 197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // at the same time 198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // while recording call stop recording not finished(status is still 199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // RECORDING), but 200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // SOUND_POWER_DOWN_MSG will exitFm(), if it is RECORDING will discard the 201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // record. 202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // 1. lose audio focus -> stop recording(lock) -> set to IDLE and show save 203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // dialog 204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // 2. exitFm() -> check the record status, discard it if it is recording 205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // status(lock) 206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Add this lock the exitFm() while stopRecording() 207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private Object mStopRecordingLock = new Object(); 208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // The listener for exit, should finish favorite when exit FM 209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static OnExitListener sExitListener = null; 210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // The latest status for mute/unmute 211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsMuted = false; 212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Audio Patch 214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private AudioPatch mAudioPatch = null; 215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private Object mRenderLock = new Object(); 216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private Notification.Builder mNotificationBuilder = null; 218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private BigTextStyle mNotificationStyle = null; 219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public IBinder onBind(Intent intent) { 222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mBinder; 223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * class use to return service instance 227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public class ServiceBinder extends Binder { 229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * get FM service instance 231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return service instance 233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmService getService() { 235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return FmService.this; 236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Broadcast monitor external event, Other app want FM stop, Phone shut 241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * down, screen state, headset state 242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private class FmServiceBroadcastReceiver extends BroadcastReceiver { 244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onReceive(Context context, Intent intent) { 247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String action = intent.getAction(); 248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String command = intent.getStringExtra("command"); 249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "onReceive, action = " + action + " / command = " + command); 250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // other app want FM stop, stop FM 251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((SOUND_POWER_DOWN_MSG.equals(action) && CMDPAUSE.equals(command))) { 252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // need remove all messages, make power down will be execute 253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeCallbacksAndMessages(null); 254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang exitFm(); 255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopSelf(); 256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // phone shut down, so exit FM 257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (Intent.ACTION_SHUTDOWN.equals(action)) { 258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * here exitFm, system will send broadcast, system will shut 260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * down, so fm does not need call back to activity 261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeCallbacksAndMessages(null); 263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang exitFm(); 264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // screen on, if FM play, open rds 265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (Intent.ACTION_SCREEN_ON.equals(action)) { 266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRdsAsync(true); 267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // screen off, if FM play, close rds 268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { 269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRdsAsync(false); 270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // switch antenna when headset plug in or plug out 271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (Intent.ACTION_HEADSET_PLUG.equals(action)) { 272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // switch antenna should not impact audio focus status 273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mValueHeadSetPlug = (intent.getIntExtra("state", -1) == HEADSET_PLUG_IN) ? 0 : 1; 274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switchAntennaAsync(mValueHeadSetPlug); 275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 276a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Avoid Service is killed,and receive headset plug in 277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // broadcast again 278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsServiceInited) { 279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "onReceive, mIsServiceInited is false"); 280a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /* 283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * If ear phone insert and activity is 284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * foreground. power up FM automatic 285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((0 == mValueHeadSetPlug) && isActivityForeground()) { 287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang powerUpAsync(FmUtils.computeFrequency(mCurrentStation)); 288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (1 == mValueHeadSetPlug) { 289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED); 290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED); 291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_TUNE_FINISHED); 292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_POWERDOWN_FINISHED); 294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_POWERUP_FINISHED); 296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang focusChanged(AudioManager.AUDIOFOCUS_LOSS); 297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Need check to switch to earphone mode for audio will 299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // change to AudioSystem.FORCE_NONE 300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setForceUse(false); 301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Notify UI change to earphone mode, false means not speaker mode 303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(2); 304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.LISTEN_SPEAKER_MODE_CHANGED); 306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_SPEAKER_MODE, false); 307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Handle sdcard mount/unmount event. 1. Update the sdcard state map 2. If 315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * the recording sdcard is unmounted, need to stop and notify 316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private class SdcardListener extends BroadcastReceiver { 318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onReceive(Context context, Intent intent) { 320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // If eject record sdcard, should set this false to not 321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // record. 322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updateSdcardStateMap(intent); 323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder == null) { 325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "SdcardListener.onReceive, mFmRecorder is null"); 326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String action = intent.getAction(); 330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (Intent.ACTION_MEDIA_EJECT.equals(action) || 331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Intent.ACTION_MEDIA_UNMOUNTED.equals(action)) { 332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // If not unmount recording sd card, do nothing; 333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRecordingCardUnmount(intent)) { 334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder.getState() == FmRecorder.STATE_RECORDING) { 335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang onRecorderError(FmRecorder.ERROR_SDCARD_NOT_PRESENT); 336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.discardRecording(); 337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(2); 339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.LISTEN_RECORDSTATE_CHANGED); 341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_RECORDING_STATE, 342a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmRecorder.STATE_IDLE); 343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 347a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 348a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * whether antenna available 353a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true, antenna available; false, antenna not available 355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isAntennaAvailable() { 357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mAudioManager.isWiredHeadsetOn(); 358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 359a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 360a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void setForceUse(boolean isSpeaker) { 361a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mForcedUseForMedia = isSpeaker ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE; 362a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioSystem.setForceUse(FOR_PROPRIETARY, mForcedUseForMedia); 363a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsSpeakerUsed = isSpeaker; 364a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 365a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 366a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 367a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set FM audio from speaker or not 368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isSpeaker true if set FM audio from speaker 370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 371a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setSpeakerPhoneOn(boolean isSpeaker) { 372a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // TODO it's on UI thread, change to sub thread 373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isSpeaker) { 374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseAudioPatch(); 375a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 376a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setForceUse(isSpeaker); 377a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 378a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isSpeaker) { 379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRender(); 380a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 381a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(true); 382a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 383a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 384a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 385a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void startRender() { 386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsRender = true; 387a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRenderLock) { 388a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderLock.notify(); 389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 390a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void stopRender() { 393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsRender = false; 394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 396a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void createRenderThread() { 397a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mRenderThread == null) { 398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderThread = new RenderThread(); 399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderThread.start(); 400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void exitRenderThread() { 404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderThread.interrupt(); 406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderThread = null; 407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private Thread mRenderThread = null; 410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private AudioRecord mAudioRecord = null; 411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private AudioTrack mAudioTrack = null; 412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int SAMPLE_RATE = 44100; 413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_CONFIGURATION_STEREO; 414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; 415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int RECORD_BUF_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE, 416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang CHANNEL_CONFIG, AUDIO_FORMAT); 417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsRender = false; 418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePort mAudioSource = null; 420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePort mAudioSink = null; 421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int mPatchVolume = 0; 422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int mPatchVolumeStep = 300; 423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isRendering() { 425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsRender; 426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void startAudioTrack() { 429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) { 430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.e(TAG, "startAudioTrack, patches count:" + patches.size()); 433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.play(); 434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void stopAudioTrack() { 438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { 439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.stop(); 440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang class RenderThread extends Thread { 444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void run() { 446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang try { 447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang byte[] buffer = new byte[RECORD_BUF_SIZE]; 448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang while (!Thread.interrupted()) { 449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean render = isRender(); 450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (render) { 451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // need rendering 452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) { 453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.play(); 454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) { 456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioRecord.startRecording(); 457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int size = mAudioRecord.read(buffer, 0, RECORD_BUF_SIZE); 459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang byte[] tmpBuf = new byte[size]; 460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang System.arraycopy(buffer, 0, tmpBuf, 0, size); 461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // write to audio track 462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.write(tmpBuf, 0, tmpBuf.length); 463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // only status wait for render 465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // stop all 466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { 467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioRecord.stop(); 468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Do not stop audio track to keep the native audio patch 471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { 472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.stop(); 473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang //enableFmAudio(true); 476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRenderLock) { 477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderLock.wait(); 478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 481a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } catch (InterruptedException e) { 482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "RenderThread.run, thread is interrupted, need exit thread"); 483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } finally { 484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { 485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioRecord.stop(); 486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { 488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.stop(); 489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // A2dp or speaker mode should render 495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isRender() { 496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mIsRender && (mPowerStatus == POWER_UP) && mIsAudioFocusHeld); 497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isSpeakerPhoneOn() { 500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mForcedUseForMedia == AudioSystem.FORCE_SPEAKER); 501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * open FM device, should be call before power up 505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if FM device open, false FM device not open 507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean openDevice() { 509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsDeviceOpen) { 510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsDeviceOpen = FmNative.openDev(); 511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsDeviceOpen; 513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * close FM device 517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if close FM device success, false close FM device failed 519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean closeDevice() { 521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isDeviceClose = false; 522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsDeviceOpen) { 523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isDeviceClose = FmNative.closeDev(); 524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsDeviceOpen = !isDeviceClose; 525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // quit looper 527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.getLooper().quit(); 528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return isDeviceClose; 529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * get FM device opened or not 533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true FM device opened, false FM device closed 535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isDeviceOpen() { 537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsDeviceOpen; 538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * power up FM, and make FM voice output from earphone 542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param frequency 544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void powerUpAsync(float frequency) { 546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED); 548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED); 549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FM_FREQUENCY, frequency); 551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_POWERUP_FINISHED); 552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean powerUp(float frequency) { 557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mWakeLock.isHeld()) { 561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.acquire(); 562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!requestAudioFocus()) { 564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // activity used for update powerdown menu 565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = POWER_DOWN; 566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = DURING_POWER_UP; 570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if device open fail when chip reset, it need open device again before 572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // power up 573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsDeviceOpen) { 574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang openDevice(); 575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!FmNative.powerUp(frequency)) { 578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = POWER_DOWN; 579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = POWER_UP; 582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // need mute after power up 583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mPowerStatus == POWER_UP); 586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean playFrequency(float frequency) { 589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mCurrentStation = FmUtils.computeStation(frequency); 590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.setCurrentStation(mContext, mCurrentStation); 591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Add notification to the title bar. 592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updatePlayingNotification(); 593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Start the RDS thread if RDS is supported. 595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRdsThread(); 597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mWakeLock.isHeld()) { 600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.acquire(); 601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsSpeakerUsed != isSpeakerPhoneOn()) { 603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setForceUse(mIsSpeakerUsed); 604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mRecordState != FmRecorder.STATE_PLAYBACK) { 606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(true); 607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(true); 610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mPowerStatus == POWER_UP); 613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * power down FM 617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void powerDownAsync() { 619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if power down Fm, should remove message first. 620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // not remove all messages, because such as recorder message need 621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // to execute after or before power down 622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED); 623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED); 624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_TUNE_FINISHED); 625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED); 626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED); 627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_POWERDOWN_FINISHED); 628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Power down FM 632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if power down success 634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean powerDown() { 636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_DOWN) { 637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(false); 642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(false); 643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!FmNative.powerDown(0)) { 645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRdsThread(); 648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mWakeLock.isHeld()) { 651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.release(); 652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Remove the notification in the title bar. 654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang removeNotification(); 655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // activity used for update powerdown menu 658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = POWER_DOWN; 659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRdsThread(); 662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mWakeLock.isHeld()) { 665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.release(); 666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Remove the notification in the title bar. 669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang removeNotification(); 670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int getPowerStatus() { 674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mPowerStatus; 675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Tune to a station 679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param frequency The frequency to tune 681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true, success; false, fail. 683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void tuneStationAsync(float frequency) { 685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_TUNE_FINISHED); 686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 688a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FM_FREQUENCY, frequency); 689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_TUNE_FINISHED); 690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 691a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 692a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 693a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 694a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean tuneStation(float frequency) { 695a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 696a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(false); 697a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean bRet = FmNative.tune(frequency); 698a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (bRet) { 699a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(true); 700a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mCurrentStation = FmUtils.computeStation(frequency); 701a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.setCurrentStation(mContext, mCurrentStation); 702a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updatePlayingNotification(); 703a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 704a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 705a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return bRet; 706a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 707a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 708a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if earphone is not insert, not power up 709a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isAntennaAvailable()) { 710a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 711a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 712a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 713a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if not power up yet, should powerup first 714a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean tune = false; 715a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 716a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (powerUp(frequency)) { 717a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang tune = playFrequency(frequency); 718a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 719a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 720a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return tune; 721a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 722a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 723a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 724a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Seek station according frequency and direction 725a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 726a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param frequency start frequency(100KHZ, 87.5) 727a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isUp direction(true, next station; false, previous station) 728a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 729a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return the frequency after seek 730a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 731a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void seekStationAsync(float frequency, boolean isUp) { 732a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED); 733a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 2; 734a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 735a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FM_FREQUENCY, frequency); 736a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(OPTION, isUp); 737a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SEEK_FINISHED); 738a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 739a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 740a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 741a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 742a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private float seekStation(float frequency, boolean isUp) { 743a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 744a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return -1; 745a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 746a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 747a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(false); 748a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsNativeSeeking = true; 749a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float fRet = FmNative.seek(frequency, isUp); 750a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsNativeSeeking = false; 751a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // make mIsStopScanCalled false, avoid stop scan make this true, 752a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // when start scan, it will return null. 753a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsStopScanCalled = false; 754a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return fRet; 755a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 756a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 757a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 758a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Scan stations 759a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 760a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void startScanAsync() { 761a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED); 762a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_SCAN_FINISHED); 763a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 764a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 765a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int[] startScan() { 766a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int[] stations = null; 767a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 768a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(false); 769a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 770a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang short[] stationsInShort = null; 771a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsStopScanCalled) { 772a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsNativeScanning = true; 773a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationsInShort = FmNative.autoScan(); 774a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsNativeScanning = false; 775a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 776a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 777a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(true); 778a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsStopScanCalled) { 779a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Received a message to power down FM, or interrupted by a phone 780a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // call. Do not return any stations. stationsInShort = null; 781a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if cancel scan, return invalid station -100 782a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationsInShort = new short[] { 783a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang -100 784a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 785a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsStopScanCalled = false; 786a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 787a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 788a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != stationsInShort) { 789a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int size = stationsInShort.length; 790a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stations = new int[size]; 791a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int i = 0; i < size; i++) { 792a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stations[i] = stationsInShort[i]; 793a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 794a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 795a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return stations; 796a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 797a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 798a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 799a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check FM Radio is in scan progress or not 800a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 801a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return if in scan progress return true, otherwise return false. 802a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 803a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isScanning() { 804a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsScanning; 805a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 806a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 807a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 808a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Stop scan progress 809a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 810a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if can stop scan, otherwise return false. 811a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 812a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean stopScan() { 813a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 814a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 815a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 816a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 817a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean bRet = false; 818a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED); 819a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED); 820a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsNativeScanning || mIsNativeSeeking) { 821a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsStopScanCalled = true; 822a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bRet = FmNative.stopScan(); 823a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 824a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return bRet; 825a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 826a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 827a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 828a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check FM is in seek progress or not 829a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 830a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if in seek progress, otherwise return false. 831a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 832a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isSeeking() { 833a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsNativeSeeking; 834a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 835a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 836a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 837a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set RDS 838a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 839a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param on true, enable RDS; false, disable RDS. 840a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 841a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setRdsAsync(boolean on) { 842a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 843a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SET_RDS_FINISHED); 844a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 845a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(OPTION, on); 846a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SET_RDS_FINISHED); 847a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 848a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 849a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 850a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 851a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int setRds(boolean on) { 852a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 853a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return -1; 854a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 855a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int ret = -1; 856a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 857a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ret = FmNative.setRds(on); 858a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 859a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setPs(""); 860a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setLRText(""); 861a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return ret; 862a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 863a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 864a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 865a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get PS information 866a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 867a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return PS information 868a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 869a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public String getPs() { 870a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mPsString; 871a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 872a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 873a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 874a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get RT information 875a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 876a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return RT information 877a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 878a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public String getRtText() { 879a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mRtTextString; 880a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 881a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 882a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 883a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get AF frequency 884a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 885a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return AF frequency 886a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 887a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void activeAfAsync() { 888a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_ACTIVE_AF_FINISHED); 889a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_ACTIVE_AF_FINISHED); 890a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 891a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 892a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int activeAf() { 893a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 894a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "activeAf, FM is not powered up"); 895a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return -1; 896a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 897a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 898a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int frequency = FmNative.activeAf(); 899a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return frequency; 900a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 901a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 902a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 903a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Mute or unmute FM voice 904a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 905a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param mute true for mute, false for unmute 906a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 907a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (true, success; false, failed) 908a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 909a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setMuteAsync(boolean mute) { 910a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SET_MUTE_FINISHED); 911a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 912a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 913a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(OPTION, mute); 914a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SET_MUTE_FINISHED); 915a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 916a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 917a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 918a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 919a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 920a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Mute or unmute FM voice 921a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 922a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param mute true for mute, false for unmute 923a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 924a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (1, success; other, failed) 925a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 926a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int setMute(boolean mute) { 927a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 928a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "setMute, FM is not powered up"); 929a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return -1; 930a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 931a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int iRet = FmNative.setMute(mute); 932a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsMuted = mute; 933a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return iRet; 934a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 935a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 936a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 937a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check the latest status is mute or not 938a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 939a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (true, mute; false, unmute) 940a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 941a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isMuted() { 942a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsMuted; 943a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 944a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 945a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 946a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check whether RDS is support in driver 947a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 948a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (true, support; false, not support) 949a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 950a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isRdsSupported() { 951a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isRdsSupported = (FmNative.isRdsSupport() == 1); 952a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return isRdsSupported; 953a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 954a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 955a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 956a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check whether speaker used or not 957a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 958a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if use speaker, otherwise return false 959a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 960a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isSpeakerUsed() { 961a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsSpeakerUsed; 962a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 963a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 964a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 965a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Initial service and current station 966a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 967a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param iCurrentStation current station frequency 968a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 969a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void initService(int iCurrentStation) { 970a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsServiceInited = true; 971a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mCurrentStation = iCurrentStation; 972a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 973a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 974a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 975a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check service is initialed or not 976a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 977a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if initialed, otherwise return false 978a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 979a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isServiceInited() { 980a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsServiceInited; 981a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 982a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 983a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 984a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get FM service current station frequency 985a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 986a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return Current station frequency 987a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 988a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int getFrequency() { 989a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mCurrentStation; 990a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 991a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 992a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 993a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set FM service station frequency 994a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 995a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param station Current station 996a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 997a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setFrequency(int station) { 998a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mCurrentStation = station; 999a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1000a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1001a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1002a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * resume FM audio 1003a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1004a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void resumeFmAudio() { 1005a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // If not check mIsAudioFocusHeld && power up, when scan canceled, 1006a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // this will be resume first, then execute power down. it will cause 1007a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // nosise. 1008a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsAudioFocusHeld && (mPowerStatus == POWER_UP)) { 1009a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(true); 1010a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1011a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1012a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1013a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1014a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Switch antenna There are two types of antenna(long and short) If long 1015a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * antenna(most is this type), must plug in earphone as antenna to receive 1016a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * FM. If short antenna, means there is a short antenna if phone already, 1017a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * can receive FM without earphone. 1018a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1019a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param antenna antenna (0, long antenna, 1 short antenna) 1020a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1021a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (0, success; 1 failed; 2 not support) 1022a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1023a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void switchAntennaAsync(int antenna) { 1024a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 1025a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SWITCH_ANNTENNA); 1026a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1027a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 1028a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.SWITCH_ANNTENNA_VALUE, antenna); 1029a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SWITCH_ANNTENNA); 1030a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 1031a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 1032a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1033a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1034a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1035a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Need native support whether antenna support interface. 1036a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1037a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param antenna antenna (0, long antenna, 1 short antenna) 1038a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1039a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (0, success; 1 failed; 2 not support) 1040a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1041a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int switchAntenna(int antenna) { 1042a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if fm not powerup, switchAntenna will flag whether has earphone 1043a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int ret = FmNative.switchAntenna(antenna); 1044a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return ret; 1045a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1046a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1047a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1048a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Start recording 1049a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1050a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void startRecordingAsync() { 1051a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_STARTRECORDING_FINISHED); 1052a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_STARTRECORDING_FINISHED); 1053a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1054a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1055a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void startRecording() { 1056a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sRecordingSdcard = FmUtils.getDefaultStoragePath(); 1057a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sRecordingSdcard == null || sRecordingSdcard.isEmpty()) { 1058a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRecording, may be no sdcard"); 1059a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang onRecorderError(FmRecorder.ERROR_SDCARD_NOT_PRESENT); 1060a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1061a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1062a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1063a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder == null) { 1064a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder = new FmRecorder(); 1065a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.registerRecorderStateListener(FmService.this); 1066a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1067a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1068a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isSdcardReady(sRecordingSdcard)) { 1069a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.startRecording(mContext); 1070a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1071a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang onRecorderError(FmRecorder.ERROR_SDCARD_NOT_PRESENT); 1072a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1073a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1074a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1075a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isSdcardReady(String sdcardPath) { 1076a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mSdcardStateMap.isEmpty()) { 1077a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mSdcardStateMap.get(sdcardPath) != null && !mSdcardStateMap.get(sdcardPath)) { 1078a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "isSdcardReady, return false"); 1079a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 1080a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1081a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1082a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 1083a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1084a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1085a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1086a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * stop recording 1087a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1088a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void stopRecordingAsync() { 1089a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_STOPRECORDING_FINISHED); 1090a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_STOPRECORDING_FINISHED); 1091a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1092a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1093a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean stopRecording() { 1094a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder == null) { 1095a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.e(TAG, "stopRecording, called without a valid recorder!!"); 1096a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 1097a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1098a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mStopRecordingLock) { 1099a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.stopRecording(); 1100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 1102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Save recording file according name or discard recording file if name is 1106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * null 1107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param newName New recording file name 1109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void saveRecordingAsync(String newName) { 1111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SAVERECORDING_FINISHED); 1112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 1113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 1114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putString(RECODING_FILE_NAME, newName); 1115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SAVERECORDING_FINISHED); 1116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 1117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 1118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void saveRecording(String newName) { 1121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 1122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (newName != null) { 1123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.saveRecording(FmService.this, newName); 1124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.discardRecording(); 1127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get record time 1132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return Record time 1134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public long getRecordTime() { 1136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 1137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mFmRecorder.getRecordTime(); 1138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return 0; 1140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set recording mode 1144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isRecording true, enter recoding mode; false, exit recording mode 1146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setRecordingModeAsync(boolean isRecording) { 1148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_RECORD_MODE_CHANED); 1149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 1150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 1151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(OPTION, isRecording); 1152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_RECORD_MODE_CHANED); 1153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 1154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 1155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void setRecordingMode(boolean isRecording) { 1158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsInRecordingMode = isRecording; 1159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 1160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isRecording) { 1161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder.getState() != FmRecorder.STATE_IDLE) { 1162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.stopRecording(); 1163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang resumeFmAudio(); 1165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 1166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // reset recorder to unused status 1169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.resetRecorder(); 1170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get current recording mode 1175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return if in recording mode return true, otherwise return false; 1177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean getRecordingMode() { 1179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsInRecordingMode; 1180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get record state 1184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return record state 1186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int getRecorderState() { 1188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mFmRecorder) { 1189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mFmRecorder.getState(); 1190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return FmRecorder.STATE_INVALID; 1192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get recording file name 1196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return recording file name 1198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public String getRecordingName() { 1200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mFmRecorder) { 1201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mFmRecorder.getRecordFileName(); 1202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return null; 1204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onCreate() { 1208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onCreate(); 1209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mContext = getApplicationContext(); 1210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 1211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 1212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); 1213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 1214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.setReferenceCounted(false); 1215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sRecordingSdcard = FmUtils.getDefaultStoragePath(); 1216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerFmBroadcastReceiver(); 1218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerSdcardReceiver(); 1219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerAudioPortUpdateListener(); 1220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang HandlerThread handlerThread = new HandlerThread("FmRadioServiceThread"); 1222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlerThread.start(); 1223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler = new FmRadioServiceHandler(handlerThread.getLooper()); 1224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang openDevice(); 1226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // set speaker to default status, avoid setting->clear data. 1227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setForceUse(mIsSpeakerUsed); 1228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang initAudioRecordSink(); 1230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang createRenderThread(); 1231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int v = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 1233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPatchVolume = computeGainVolume(v); 1234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private MediaSession mSession; 1237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private PlaybackState mPlaybackState; 1238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private MediaSession.Callback mSessionCallback; 1239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void createMediaSession() { 1241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_RELATIVE, 15, 7) { 1242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onSetVolumeTo(int volume) { 1243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPatchVolume = computeGainVolume(volume); 1244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang adjustAudioGain(mPatchVolume); 1245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onAdjustVolume(int direction) { 1248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (direction == 0) { 1249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int current = getCurrentVolume() + direction; 1253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (current <= getMaxVolume() && current >= 0) { 1254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setCurrentVolume(current); 1255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, direction, 0); 1256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int v = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 1257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (direction > 0) { 1258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang volumeUp(); 1259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang volumeDown(); 1261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 1265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSessionCallback = new SessionCb(); 1267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang PlaybackState.Builder psBob = new PlaybackState.Builder(); 1268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPlaybackState = psBob.setState(PlaybackState.STATE_PLAYING, 0, 0).build(); 1269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang MediaSessionManager man = (MediaSessionManager) mContext 1270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang .getSystemService(Context.MEDIA_SESSION_SERVICE); 1271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSession = new MediaSession(mContext, "OneMedia"); 1272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang MediaController controller = mSession.getController(); 1273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSession.setCallback(mSessionCallback); 1274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSession.setPlaybackState(mPlaybackState); 1275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS 1276a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 1277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSession.setActive(true); 1278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSession.setPlaybackToRemote(vp); 1279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1280a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private class SessionCb extends MediaSession.Callback { 1282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onPlay() { 1284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onPause() { 1288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onCommand(String command, Bundle args, ResultReceiver cb) { 1292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onCommand(command, args, cb); 1293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean onMediaButtonEvent(Intent intent) { 1297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return super.onMediaButtonEvent(intent); 1298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void releaseMediaSession() { 1302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mSession != null) { 1303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSession.release(); 1304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSession = null; 1305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void registerAudioPortUpdateListener() { 1309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPortUpdateListener == null) { 1310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioPortUpdateListener = new FmOnAudioPortUpdateListener(); 1311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.registerAudioPortUpdateListener(mAudioPortUpdateListener); 1312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void unregisterAudioPortUpdateListener() { 1316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPortUpdateListener != null) { 1317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.unregisterAudioPortUpdateListener(mAudioPortUpdateListener); 1318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioPortUpdateListener = null; 1319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void initAudioRecordSink() { 1323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.FM_TUNER, 1324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, RECORD_BUF_SIZE); 1325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 1326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, RECORD_BUF_SIZE, AudioTrack.MODE_STREAM); 1327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void createAudioPatch() { 1330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch != null) { 1331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "createAudioPatch, mAudioPatch is not null, return"); 1332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSource = null; 1336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSink = null; 1337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); 1338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPorts(ports); 1339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (AudioPort port : ports) { 1340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (port instanceof AudioDevicePort) { 1341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int type = ((AudioDevicePort) port).type(); 1342a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String name = AudioSystem.getOutputDeviceName(type); 1343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (type == AudioSystem.DEVICE_IN_FM_TUNER) { 1344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSource = (AudioDevicePort) port; 1345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (type == AudioSystem.DEVICE_OUT_WIRED_HEADSET || 1346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang type == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) { 1347a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSink = (AudioDevicePort) port; 1348a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioSource != null && mAudioSink != null) { 1352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePortConfig sourceConfig = (AudioDevicePortConfig) mAudioSource 1353a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang .activeConfig(); 1354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePortConfig sinkConfig = (AudioDevicePortConfig) mAudioSink.activeConfig(); 1355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPatch[] audioPatchArray = new AudioPatch[] {null}; 1356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.createAudioPatch(audioPatchArray, 1357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new AudioPortConfig[] {sourceConfig}, 1358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new AudioPortConfig[] {sinkConfig}); 1359a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioPatch = audioPatchArray[0]; 1360a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang adjustAudioGain(mPatchVolume); 1361a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang createMediaSession(); 1362a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1363a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1364a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1365a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void adjustAudioGain(int volume) { 1366a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch == null || mAudioSource == null || mAudioSink == null) { 1367a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioGainConfig sinkGainConfig = null; 1371a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioSink.gains().length > 0) { 1372a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioGain sinkGain = null; 1373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (AudioGain gain : mAudioSink.gains()) { 1374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((gain.mode() & AudioGain.MODE_JOINT) != 0) { 1375a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sinkGain = gain; 1376a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 1377a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1378a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1380a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // NOTE: we only change the source gain in MODE_JOINT here. 1381a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sinkGain != null) { 1382a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int numChannels = 0; 1383a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int mask = sinkGain.channelMask(); mask > 0; mask >>= 1) { 1384a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang numChannels += (mask & 1); 1385a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int[] gainValues = new int[numChannels]; 1387a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Arrays.fill(gainValues, volume); 1388a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sinkGainConfig = sinkGain.buildConfig(AudioGain.MODE_JOINT, 1389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 0x08, gainValues, 0); 1390a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePortConfig sinkConfig = mAudioSink.buildConfig(SAMPLE_RATE, 1394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang CHANNEL_CONFIG, AUDIO_FORMAT, sinkGainConfig); 1395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.setAudioPortGain(mAudioSink, sinkGainConfig); 1396a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1397a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // 0 ~ 15 1399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int computeGainVolume(int streamVolume) { 1400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return 0 - (15 - streamVolume) * mPatchVolumeStep; 1401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private FmOnAudioPortUpdateListener mAudioPortUpdateListener = null; 1404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private class FmOnAudioPortUpdateListener implements OnAudioPortUpdateListener { 1406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Callback method called upon audio port list update. 1408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param portList the updated list of audio ports 1409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onAudioPortListUpdate(AudioPort[] portList) { 1412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 1413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "onAudioPortListUpdate, not power up, return"); 1414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsAudioFocusHeld) { 1417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "onAudioPortListUpdate, current not available return." 1418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang + "mIsAudioFocusHeld:" + mIsAudioFocusHeld); 1419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch != null) { 1423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startAudioTrack(); 1424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 1425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 1426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isPatchMixerToEarphone(patches)) { 1427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopAudioTrack(); 1428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseAudioPatch(); 1431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRender(); 1432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Callback method called upon audio patch list update. 1438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param patchList the updated list of audio patches 1440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onAudioPatchListUpdate(AudioPatch[] patchList) { 1443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 1444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "onAudioPortListUpdate, not power up"); 1445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsAudioFocusHeld) { 1449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "onAudioPortListUpdate, Current not available return." 1450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang + "mIsAudioFocusHeld:" + mIsAudioFocusHeld); 1451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch != null) { 1455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startAudioTrack(); 1456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 1457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 1458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isPatchMixerToEarphone(patches)) { 1459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopAudioTrack(); 1460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseAudioPatch(); 1463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRender(); 1464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (mIsRender) { 1466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 1467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 1468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isPatchMixerToEarphone(patches)) { 1469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopAudioTrack(); 1470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang createAudioPatch(); 1472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Callback method called when the mediaserver dies 1478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onServiceDied() { 1481a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(false); 1482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void releaseAudioPatch() { 1486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch != null) { 1487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.releaseAudioPatch(mAudioPatch); 1488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioPatch = null; 1489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseMediaSession(); 1490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSource = null; 1492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSink = null; 1493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void registerFmBroadcastReceiver() { 1496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang IntentFilter filter = new IntentFilter(); 1497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(SOUND_POWER_DOWN_MSG); 1498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_SHUTDOWN); 1499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_SCREEN_ON); 1500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_SCREEN_OFF); 1501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_HEADSET_PLUG); 1502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mBroadcastReceiver = new FmServiceBroadcastReceiver(); 1503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerReceiver(mBroadcastReceiver, filter); 1504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void unregisterFmBroadcastReceiver() { 1507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mBroadcastReceiver) { 1508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterReceiver(mBroadcastReceiver); 1509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mBroadcastReceiver = null; 1510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onDestroy() { 1515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.setParameters("AudioFmPreStop=1"); 1516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 1517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // stop rds first, avoid blocking other native method 1518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 1519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRdsThread(); 1520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterFmBroadcastReceiver(); 1522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterSdcardListener(); 1523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang abandonAudioFocus(); 1524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang exitFm(); 1525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mFmRecorder) { 1526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder = null; 1527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang exitRenderThread(); 1529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseAudioPatch(); 1530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseMediaSession(); 1531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterAudioPortUpdateListener(); 1532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onDestroy(); 1533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Exit FMRadio application 1537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void exitFm() { 1539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsAudioFocusHeld = false; 1540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Stop FM recorder if it is working 1541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mFmRecorder) { 1542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mStopRecordingLock) { 1543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int fmState = mFmRecorder.getState(); 1544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmRecorder.STATE_RECORDING == fmState) { 1545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.stopRecording(); 1546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // When exit, we set the audio path back to earphone. 1551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsNativeScanning || mIsNativeSeeking) { 1552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopScan(); 1553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeCallbacksAndMessages(null); 1556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_FM_EXIT); 1557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_FM_EXIT); 1558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onConfigurationChanged(Configuration newConfig) { 1562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onConfigurationChanged(newConfig); 1563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Change the notification string. 1564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 1565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang showPlayingNotification(); 1566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int onStartCommand(Intent intent, int flags, int startId) { 1571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int ret = super.onStartCommand(intent, flags, startId); 1572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (intent != null) { 1574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String action = intent.getAction(); 1575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FM_SEEK_PREVIOUS.equals(action)) { 1576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang seekStationAsync(FmUtils.computeFrequency(mCurrentStation), false); 1577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (FM_SEEK_NEXT.equals(action)) { 1578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang seekStationAsync(FmUtils.computeFrequency(mCurrentStation), true); 1579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (FM_TURN_OFF.equals(action)) { 1580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang powerDownAsync(); 1581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return START_NOT_STICKY; 1584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Start RDS thread to update RDS information 1588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void startRdsThread() { 1590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsRdsThreadExit = false; 1591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mRdsThread) { 1592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRdsThread = new Thread() { 1595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void run() { 1596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang while (true) { 1597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsRdsThreadExit) { 1598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 1599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int iRdsEvents = FmNative.readRds(); 1602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (iRdsEvents != 0) { 1603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRdsThread, is rds events: " + iRdsEvents); 1604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (RDS_EVENT_PROGRAMNAME == (RDS_EVENT_PROGRAMNAME & iRdsEvents)) { 1607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang byte[] bytePS = FmNative.getPs(); 1608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != bytePS) { 1609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String ps = new String(bytePS).trim(); 1610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mPsString.equals(ps)) { 1611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updatePlayingNotification(); 1612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ContentValues values = null; 1614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmStation.isStationExist(mContext, mCurrentStation)) { 1615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values = new ContentValues(1); 1616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.PROGRAM_SERVICE, ps); 1617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.updateStationToDb(mContext, mCurrentStation, values); 1618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values = new ContentValues(2); 1620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.FREQUENCY, mCurrentStation); 1621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.PROGRAM_SERVICE, ps); 1622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.insertStationToDb(mContext, values); 1623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setPs(ps); 1625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (RDS_EVENT_LAST_RADIOTEXT == (RDS_EVENT_LAST_RADIOTEXT & iRdsEvents)) { 1629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang byte[] byteLRText = FmNative.getLrText(); 1630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != byteLRText) { 1631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String rds = new String(byteLRText).trim(); 1632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setLRText(rds); 1633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ContentValues values = null; 1634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmStation.isStationExist(mContext, mCurrentStation)) { 1635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values = new ContentValues(1); 1636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.RADIO_TEXT, rds); 1637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.updateStationToDb(mContext, mCurrentStation, values); 1638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values = new ContentValues(2); 1640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.FREQUENCY, mCurrentStation); 1641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.RADIO_TEXT, rds); 1642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.insertStationToDb(mContext, values); 1643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (RDS_EVENT_AF == (RDS_EVENT_AF & iRdsEvents)) { 1648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /* 1649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * add for rds AF 1650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsScanning || mIsSeeking) { 1652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRdsThread, seek or scan going, no need to tune here"); 1653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (mPowerStatus == POWER_DOWN) { 1654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRdsThread, fm is power down, do nothing."); 1655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int iFreq = FmNative.activeAf(); 1657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(iFreq)) { 1658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if the new frequency is not equal to current 1659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // frequency. 1660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mCurrentStation != iFreq) { 1661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsScanning && !mIsSeeking) { 1662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRdsThread, seek or scan not going," 1663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang + "need to tune here"); 1664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang tuneStationAsync(FmUtils.computeFrequency(iFreq)); 1665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Do not handle other events. 1671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Sleep 500ms to reduce inquiry frequency 1672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang try { 1673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int hundredMillisecond = 500; 1674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Thread.sleep(hundredMillisecond); 1675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } catch (InterruptedException e) { 1676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang e.printStackTrace(); 1677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 1681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRdsThread.start(); 1682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Stop RDS thread to stop listen station RDS change 1686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void stopRdsThread() { 1688a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mRdsThread) { 1689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Must call closedev after stopRDSThread. 1690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsRdsThreadExit = true; 1691a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRdsThread = null; 1692a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1693a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1694a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1695a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1696a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set PS information 1697a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1698a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param ps The ps information 1699a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1700a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void setPs(String ps) { 1701a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (0 != mPsString.compareTo(ps)) { 1702a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPsString = ps; 1703a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(3); 1704a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_PS_CHANGED); 1705a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putString(FmListener.KEY_PS_INFO, mPsString); 1706a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 1707a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } // else New PS is the same as current 1708a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1709a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1710a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1711a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set RT information 1712a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1713a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param lrtText The RT information 1714a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1715a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void setLRText(String lrtText) { 1716a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (0 != mRtTextString.compareTo(lrtText)) { 1717a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRtTextString = lrtText; 1718a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(3); 1719a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RT_CHANGED); 1720a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putString(FmListener.KEY_RT_INFO, mRtTextString); 1721a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 1722a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } // else New RT is the same as current 1723a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1724a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1725a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1726a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Open or close FM Radio audio 1727a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1728a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param enable true, open FM audio; false, close FM audio; 1729a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1730a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void enableFmAudio(boolean enable) { 1731a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (enable) { 1732a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((mPowerStatus != POWER_UP) || !mIsAudioFocusHeld) { 1733a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "enableFmAudio, current not available return.mIsAudioFocusHeld:" 1734a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang + mIsAudioFocusHeld); 1735a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1736a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1737a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1738a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startAudioTrack(); 1739a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 1740a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 1741a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch == null) { 1742a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isPatchMixerToEarphone(patches)) { 1743a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopAudioTrack(); 1744a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1745a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang createAudioPatch(); 1746a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1747a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRender(); 1748a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1749a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1750a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1751a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseAudioPatch(); 1752a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1753a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1754a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1755a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1756a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Make sure patches count will not be 0 1757a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isPatchMixerToEarphone(ArrayList<AudioPatch> patches) { 1758a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int deviceCount = 0; 1759a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int deviceEarphoneCount = 0; 1760a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (AudioPatch patch : patches) { 1761a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPortConfig[] sources = patch.sources(); 1762a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPortConfig[] sinks = patch.sinks(); 1763a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPortConfig sourceConfig = sources[0]; 1764a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPortConfig sinkConfig = sinks[0]; 1765a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPort sourcePort = sourceConfig.port(); 1766a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPort sinkPort = sinkConfig.port(); 1767a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sourcePort instanceof AudioMixPort && sinkPort instanceof AudioDevicePort) { 1768a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang deviceCount++; 1769a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int type = ((AudioDevicePort) sinkPort).type(); 1770a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (type == AudioSystem.DEVICE_OUT_WIRED_HEADSET || 1771a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang type == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) { 1772a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang deviceEarphoneCount++; 1773a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1774a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1775a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1776a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (deviceEarphoneCount == 1 && deviceCount == deviceEarphoneCount) { 1777a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 1778a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1779a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 1780a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1781a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1782a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1783a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Show notification 1784a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1785a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void showPlayingNotification() { 1786a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isActivityForeground() || mIsScanning 1787a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang || (getRecorderState() == FmRecorder.STATE_RECORDING)) { 1788a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "showPlayingNotification, do not show main notification."); 1789a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1790a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1791a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String stationName = ""; 1792a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String ratioText = ""; 1793a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ContentResolver resolver = mContext.getContentResolver(); 1794a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Cursor cursor = null; 1795a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang try { 1796a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang cursor = resolver.query( 1797a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Station.CONTENT_URI, 1798a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.COLUMNS, 1799a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Station.FREQUENCY + "=?", 1800a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new String[] { String.valueOf(mCurrentStation) }, 1801a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang null); 1802a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (cursor != null && cursor.moveToFirst()) { 1803a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // If the station name is not exist, show program service(PS) instead 1804a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationName = cursor.getString(cursor.getColumnIndex(Station.STATION_NAME)); 1805a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (TextUtils.isEmpty(stationName)) { 1806a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationName = cursor.getString(cursor.getColumnIndex(Station.PROGRAM_SERVICE)); 1807a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1808a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ratioText = cursor.getString(cursor.getColumnIndex(Station.RADIO_TEXT)); 1809a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1810a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1811a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "showPlayingNotification, cursor is null"); 1812a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1813a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } finally { 1814a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (cursor != null) { 1815a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang cursor.close(); 1816a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1817a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1818a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1819a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Intent aIntent = new Intent(Intent.ACTION_MAIN); 1820a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang aIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1821a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1822a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang aIntent.setClassName(getPackageName(), mTargetClassName); 1823a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang PendingIntent pAIntent = PendingIntent.getActivity(mContext, 0, aIntent, 0); 1824a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1825a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null == mNotificationBuilder) { 1826a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder = new Notification.Builder(mContext); 1827a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setSmallIcon(R.drawable.ic_launcher); 1828a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setShowWhen(false); 1829a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setAutoCancel(true); 1830a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1831a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Intent intent = new Intent(FM_SEEK_PREVIOUS); 1832a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent.setClass(mContext, FmService.class); 1833a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang PendingIntent pIntent = PendingIntent.getService(mContext, 0, intent, 0); 1834a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.addAction(R.drawable.btn_fm_prevstation, "", pIntent); 1835a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent = new Intent(FM_TURN_OFF); 1836a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent.setClass(mContext, FmService.class); 1837a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang pIntent = PendingIntent.getService(mContext, 0, intent, 0); 1838a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.addAction(R.drawable.btn_fm_rec_stop_enabled, "", pIntent); 1839a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent = new Intent(FM_SEEK_NEXT); 1840a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent.setClass(mContext, FmService.class); 1841a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang pIntent = PendingIntent.getService(mContext, 0, intent, 0); 1842a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.addAction(R.drawable.btn_fm_nextstation, "", pIntent); 1843a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1844a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setContentIntent(pAIntent); 1845a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bitmap largeIcon = FmUtils.createNotificationLargeIcon(mContext, 1846a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmUtils.formatStation(mCurrentStation)); 1847a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setLargeIcon(largeIcon); 1848a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Show FM Radio if empty 1849a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (TextUtils.isEmpty(stationName)) { 1850a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationName = getString(R.string.app_name); 1851a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1852a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setContentTitle(stationName); 1853a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!TextUtils.isEmpty(ratioText)) { 1854a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setContentText(ratioText); 1855a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1856a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1857a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Notification n = mNotificationBuilder.build(); 1858a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang n.flags &= ~Notification.FLAG_NO_CLEAR; 1859a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startForeground(NOTIFICATION_ID, n); 1860a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1861a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1862a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1863a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Show notification 1864a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1865a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void showRecordingNotification(Notification notification) { 1866a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startForeground(NOTIFICATION_ID, notification); 1867a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1868a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1869a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1870a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Remove notification 1871a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1872a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void removeNotification() { 1873a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopForeground(true); 1874a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1875a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1876a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1877a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Update notification 1878a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1879a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void updatePlayingNotification() { 1880a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 1881a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang showPlayingNotification(); 1882a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1883a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1884a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1885a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1886a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Register sdcard listener for record 1887a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1888a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void registerSdcardReceiver() { 1889a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mSdcardListener == null) { 1890a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSdcardListener = new SdcardListener(); 1891a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1892a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang IntentFilter filter = new IntentFilter(); 1893a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addDataScheme("file"); 1894a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_MEDIA_MOUNTED); 1895a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); 1896a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_MEDIA_EJECT); 1897a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerReceiver(mSdcardListener, filter); 1898a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1899a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1900a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void unregisterSdcardListener() { 1901a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mSdcardListener) { 1902a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterReceiver(mSdcardListener); 1903a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1904a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1905a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1906a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void updateSdcardStateMap(Intent intent) { 1907a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String action = intent.getAction(); 1908a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String sdcardPath = null; 1909a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Uri mountPointUri = intent.getData(); 1910a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mountPointUri != null) { 1911a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sdcardPath = mountPointUri.getPath(); 1912a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sdcardPath != null) { 1913a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (Intent.ACTION_MEDIA_EJECT.equals(action)) { 1914a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSdcardStateMap.put(sdcardPath, false); 1915a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (Intent.ACTION_MEDIA_UNMOUNTED.equals(action)) { 1916a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSdcardStateMap.put(sdcardPath, false); 1917a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) { 1918a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSdcardStateMap.put(sdcardPath, true); 1919a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1920a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1921a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1922a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1923a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1924a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1925a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Notify FM recorder state 1926a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1927a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param state The current FM recorder state 1928a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1929a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1930a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onRecorderStateChanged(int state) { 1931a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRecordState = state; 1932a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(2); 1933a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RECORDSTATE_CHANGED); 1934a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_RECORDING_STATE, state); 1935a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 1936a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1937a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1938a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1939a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Notify FM recorder error message 1940a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1941a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param error The recorder error type 1942a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1943a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1944a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onRecorderError(int error) { 1945a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if media server die, will not enable FM audio, and convert to 1946a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // ERROR_PLAYER_INATERNAL, call back to activity showing toast. 1947a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRecorderErrorType = error; 1948a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1949a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(2); 1950a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RECORDERROR); 1951a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_RECORDING_ERROR_TYPE, mRecorderErrorType); 1952a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 1953a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1954a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1955a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1956a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check and go next(play or show tips) after recorder file play 1957a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * back finish. 1958a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Two cases: 1959a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1. With headset -> play FM 1960a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2. Without headset -> show plug in earphone tips 1961a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1962a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void checkState() { 1963a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isHeadSetIn()) { 1964a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // with headset 1965a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 1966a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang resumeFmAudio(); 1967a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 1968a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1969a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang powerUpAsync(FmUtils.computeFrequency(mCurrentStation)); 1970a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1971a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1972a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // without headset need show plug in earphone tips 1973a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switchAntennaAsync(mValueHeadSetPlug); 1974a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1975a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1976a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1977a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1978a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check the headset is plug in or plug out 1979a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1980a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true for plug in; false for plug out 1981a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1982a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isHeadSetIn() { 1983a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (0 == mValueHeadSetPlug); 1984a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1985a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1986a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void focusChanged(int focusState) { 1987a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsAudioFocusHeld = false; 1988a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsNativeScanning || mIsNativeSeeking) { 1989a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // make stop scan from activity call to service. 1990a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // notifyActivityStateChanged(FMRadioListener.LISTEN_SCAN_CANCELED); 1991a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopScan(); 1992a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1993a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1994a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // using handler thread to update audio focus state 1995a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updateAudioFocusAync(focusState); 1996a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1997a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1998a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1999a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Request audio focus 2000a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2001a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true, success; false, fail; 2002a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2003a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean requestAudioFocus() { 2004a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsAudioFocusHeld) { 2005a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 2006a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2007a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2008a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int audioFocus = mAudioManager.requestAudioFocus(mAudioFocusChangeListener, 2009a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); 2010a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsAudioFocusHeld = (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == audioFocus); 2011a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsAudioFocusHeld; 2012a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2013a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2014a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2015a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Abandon audio focus 2016a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2017a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void abandonAudioFocus() { 2018a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.abandonAudioFocus(mAudioFocusChangeListener); 2019a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsAudioFocusHeld = false; 2020a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2021a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2022a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2023a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Use to interact with other voice related app 2024a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2025a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private final OnAudioFocusChangeListener mAudioFocusChangeListener = 2026a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new OnAudioFocusChangeListener() { 2027a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2028a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Handle audio focus change ensure message FIFO 2029a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2030a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param focusChange audio focus change state 2031a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2032a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 2033a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onAudioFocusChange(int focusChange) { 2034a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switch (focusChange) { 2035a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_LOSS: 2036a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (this) { 2037a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.setParameters("AudioFmPreStop=1"); 2038a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 2039a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang focusChanged(AudioManager.AUDIOFOCUS_LOSS); 2040a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2041a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2042a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2043a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 2044a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (this) { 2045a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.setParameters("AudioFmPreStop=1"); 2046a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 2047a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang focusChanged(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT); 2048a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2049a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2050a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2051a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_GAIN: 2052a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (this) { 2053a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updateAudioFocusAync(AudioManager.AUDIOFOCUS_GAIN); 2054a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2055a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2056a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2057a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang default: 2058a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2059a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2060a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2061a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 2062a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2063a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2064a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Audio focus changed, will send message to handler thread. synchronized to 2065a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * ensure one message can go in this method. 2066a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2067a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param focusState AudioManager state 2068a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2069a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void updateAudioFocusAync(int focusState) { 2070a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 2071a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 2072a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_AUDIOFOCUS_CHANGED, focusState); 2073a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_AUDIOFOCUS_CHANGED); 2074a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 2075a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 2076a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2077a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2078a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2079a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Audio focus changed, update FM focus state. 2080a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2081a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param focusState AudioManager state 2082a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2083a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void updateAudioFocus(int focusState) { 2084a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switch (focusState) { 2085a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_LOSS: 2086a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPausedByTransientLossOfFocus = false; 2087a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // play back audio will output with music audio 2088a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // May be affect other recorder app, but the flow can not be 2089a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // execute earlier, 2090a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // It should ensure execute after start/stop record. 2091a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 2092a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int fmState = mFmRecorder.getState(); 2093a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // only handle recorder state, not handle playback state 2094a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (fmState == FmRecorder.STATE_RECORDING) { 2095a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 2096a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_STARTRECORDING_FINISHED); 2097a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 2098a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_STOPRECORDING_FINISHED); 2099a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRecording(); 2100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerDown(); 2103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 2106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 2107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPausedByTransientLossOfFocus = true; 2108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // play back audio will output with music audio 2110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // May be affect other recorder app, but the flow can not be 2111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // execute earlier, 2112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // It should ensure execute after start/stop record. 2113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 2114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int fmState = mFmRecorder.getState(); 2115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (fmState == FmRecorder.STATE_RECORDING) { 2116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 2117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_STARTRECORDING_FINISHED); 2118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 2119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_STOPRECORDING_FINISHED); 2120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRecording(); 2121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerDown(); 2124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_GAIN: 2127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((mPowerStatus != POWER_UP) && mPausedByTransientLossOfFocus) { 2128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 2129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED); 2130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED); 2131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 2132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FM_FREQUENCY, FmUtils.computeFrequency(mCurrentStation)); 2133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerUp(bundle); 2134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang default: 2138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * FM Radio listener record 2144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static class Record { 2146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int mHashCode; // hash code 2147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener mCallback; // call back 2148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Register FM Radio listener, activity get service state should call this 2152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * method register FM Radio listener 2153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param callback FM Radio listener 2155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void registerFmRadioListener(FmListener callback) { 2157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRecords) { 2158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // register callback in AudioProfileService, if the callback is 2159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // exist, just replace the event. 2160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Record record = null; 2161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int hashCode = callback.hashCode(); 2162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int n = mRecords.size(); 2163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int i = 0; i < n; i++) { 2164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang record = mRecords.get(i); 2165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (hashCode == record.mHashCode) { 2166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 2167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang record = new Record(); 2170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang record.mHashCode = hashCode; 2171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang record.mCallback = callback; 2172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRecords.add(record); 2173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Call back from service to activity 2178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param bundle The message to activity 2180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void notifyActivityStateChanged(Bundle bundle) { 2182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mRecords.isEmpty()) { 2183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRecords) { 2184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Iterator<Record> iterator = mRecords.iterator(); 2185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang while (iterator.hasNext()) { 2186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Record record = (Record) iterator.next(); 2187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener listener = record.mCallback; 2189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (listener == null) { 2191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang iterator.remove(); 2192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 2193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang listener.onCallBack(bundle); 2196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Call back from service to the current request activity 2203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Scan need only notify FmFavoriteActivity if current is FmFavoriteActivity 2204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param bundle The message to activity 2206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void notifyCurrentActivityStateChanged(Bundle bundle) { 2208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mRecords.isEmpty()) { 2209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "notifyCurrentActivityStateChanged = " + mRecords.size()); 2210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRecords) { 2211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mRecords.size() > 0) { 2212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Record record = mRecords.get(mRecords.size() - 1); 2213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener listener = record.mCallback; 2214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (listener == null) { 2215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRecords.remove(record); 2216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 2217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang listener.onCallBack(bundle); 2219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Unregister FM Radio listener 2226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param callback FM Radio listener 2228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void unregisterFmRadioListener(FmListener callback) { 2230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang remove(callback.hashCode()); 2231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Remove call back according hash code 2235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param hashCode The call back hash code 2237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void remove(int hashCode) { 2239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRecords) { 2240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Iterator<Record> iterator = mRecords.iterator(); 2241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang while (iterator.hasNext()) { 2242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Record record = (Record) iterator.next(); 2243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (record.mHashCode == hashCode) { 2244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang iterator.remove(); 2245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check recording sd card is unmount 2252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param intent The unmount sd card intent 2254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true or false indicate whether current recording sd card is 2256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * unmount or not 2257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isRecordingCardUnmount(Intent intent) { 2259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String unmountSDCard = intent.getData().toString(); 2260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "unmount sd card file path: " + unmountSDCard); 2261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return unmountSDCard.equalsIgnoreCase("file://" + sRecordingSdcard) ? true : false; 2262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int[] updateStations(int[] stations) { 2265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "updateStations.firstValidstation:" + Arrays.toString(stations)); 2266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int firstValidstation = mCurrentStation; 2267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int stationNum = 0; 2269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != stations) { 2270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int searchedListSize = stations.length; 2271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsDistanceExceed) { 2272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.cleanSearchedStations(mContext); 2273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int j = 0; j < searchedListSize; j++) { 2274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqSearched = stations[j]; 2275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(freqSearched) && 2276a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang !FmStation.isFavoriteStation(mContext, freqSearched)) { 2277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.insertStationToDb(mContext, freqSearched, null); 2278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2280a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // get stations from db 2282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationNum = updateDBInLocation(stations); 2283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "updateStations.firstValidstation:" + firstValidstation + 2287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ",stationNum:" + stationNum); 2288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (new int[] { 2289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang firstValidstation, stationNum 2290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }); 2291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * update DB, keep favorite and rds which is searched this time, 2295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * delete rds from db which is not searched this time. 2296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param stations 2297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return number of valid searched stations 2298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int updateDBInLocation(int[] stations) { 2300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int stationNum = 0; 2301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int searchedListSize = stations.length; 2302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<Integer> stationsInDB = new ArrayList<Integer>(); 2303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Cursor cursor = null; 2304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang try { 2305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // get non favorite stations 2306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang cursor = mContext.getContentResolver().query(Station.CONTENT_URI, 2307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new String[] { FmStation.Station.FREQUENCY }, 2308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.Station.IS_FAVORITE + "=0", 2309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang null, FmStation.Station.FREQUENCY); 2310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((null != cursor) && cursor.moveToFirst()) { 2311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang do { 2313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqInDB = cursor.getInt(cursor.getColumnIndex( 2314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.Station.FREQUENCY)); 2315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationsInDB.add(freqInDB); 2316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } while (cursor.moveToNext()); 2317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "updateDBInLocation, insertSearchedStation cursor is null"); 2320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } finally { 2322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != cursor) { 2323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang cursor.close(); 2324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int listSizeInDB = stationsInDB.size(); 2328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // delete station if db frequency is not in searched list 2329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int i = 0; i < listSizeInDB; i++) { 2330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqInDB = stationsInDB.get(i); 2331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int j = 0; j < searchedListSize; j++) { 2332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqSearched = stations[j]; 2333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (freqInDB == freqSearched) { 2334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (j == (searchedListSize - 1) && freqInDB != freqSearched) { 2337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // delete from db 2338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.deleteStationInDb(mContext, freqInDB); 2339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2342a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // add to db if station is not in db 2344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int j = 0; j < searchedListSize; j++) { 2345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqSearched = stations[j]; 2346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(freqSearched)) { 2347a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationNum++; 2348a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!stationsInDB.contains(freqSearched) 2349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang && !FmStation.isFavoriteStation(mContext, freqSearched)) { 2350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // insert to db 2351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.insertStationToDb(mContext, freqSearched, ""); 2352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2353a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return stationNum; 2356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2359a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * The background handler 2360a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2361a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang class FmRadioServiceHandler extends Handler { 2362a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public FmRadioServiceHandler(Looper looper) { 2363a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super(looper); 2364a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2365a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2366a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 2367a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void handleMessage(Message msg) { 2368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle; 2369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isPowerup = false; 2370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isSwitch = true; 2371a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2372a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switch (msg.what) { 2373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // power up 2375a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_POWERUP_FINISHED: 2376a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2377a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerUp(bundle); 2378a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2380a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // power down 2381a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_POWERDOWN_FINISHED: 2382a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerDown(); 2383a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2384a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2385a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // fm exit 2386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_FM_EXIT: 2387a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsSpeakerUsed) { 2388a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setForceUse(false); 2389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2390a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang powerDown(); 2391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang closeDevice(); 2392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(1); 2394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_FM_EXIT); 2395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2396a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Finish favorite when exit FM 2397a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sExitListener != null) { 2398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sExitListener.onExit(); 2399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // switch antenna 2403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SWITCH_ANNTENNA: 2404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int value = bundle.getInt(FmListener.SWITCH_ANNTENNA_VALUE); 2406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if ear phone insert, need dismiss plugin earphone 2408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // dialog 2409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if earphone plug out and it is not play recorder 2410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // state, show plug dialog. 2411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (0 == value) { 2412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // powerUpAsync(FMRadioUtils.computeFrequency(mCurrentStation)); 2413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 2414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_SWITCH_ANNTENNA); 2415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANNTENNA, true); 2416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // ear phone plug out, and recorder state is not 2419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // play recorder state, 2420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // show dialog. 2421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mRecordState != FmRecorder.STATE_PLAYBACK) { 2422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 2423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_SWITCH_ANNTENNA); 2424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANNTENNA, false); 2425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // tune to station 2431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_TUNE_FINISHED: 2432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float tuneStation = bundle.getFloat(FM_FREQUENCY); 2434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isTune = tuneStation(tuneStation); 2435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if tune fail, pass current station to update ui 2436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isTune) { 2437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang tuneStation = FmUtils.computeFrequency(mCurrentStation); 2438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(3); 2440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 2441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_TUNE_FINISHED); 2442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_TUNE, isTune); 2443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FmListener.KEY_TUNE_TO_STATION, tuneStation); 2444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // seek to station 2448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SEEK_FINISHED: 2449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsSeeking = true; 2451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float seekStation = seekStation(bundle.getFloat(FM_FREQUENCY), 2452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.getBoolean(OPTION)); 2453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isStationTunningSuccessed = false; 2454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int station = FmUtils.computeStation(seekStation); 2455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(station)) { 2456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isStationTunningSuccessed = tuneStation(seekStation); 2457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if tune fail, pass current station to update ui 2459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isStationTunningSuccessed) { 2460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang seekStation = FmUtils.computeFrequency(mCurrentStation); 2461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(2); 2463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 2464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_TUNE_FINISHED); 2465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_TUNE, isStationTunningSuccessed); 2466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FmListener.KEY_TUNE_TO_STATION, seekStation); 2467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsSeeking = false; 2469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // start scan 2472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SCAN_FINISHED: 2473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int[] stations = null; 2474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int[] result = null; 2475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int scanTuneStation = 0; 2476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isScan = true; 2477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsScanning = true; 2478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (powerUp(FmUtils.DEFAULT_STATION_FLOAT)) { 2479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stations = startScan(); 2480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2481a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // check whether cancel scan 2483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((null != stations) && stations[0] == -100) { 2484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isScan = false; 2485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang result = new int[] { 2486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang -1, 0 2487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 2488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang result = updateStations(stations); 2490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang scanTuneStation = result[0]; 2491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang tuneStation(FmUtils.computeFrequency(mCurrentStation)); 2492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /* 2495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * if there is stop command when scan, so it needs to mute 2496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * fm avoid fm sound come out. 2497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsAudioFocusHeld) { 2499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 2500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(4); 2502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 2503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_SCAN_FINISHED); 2504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang //bundle.putInt(FmListener.KEY_TUNE_TO_STATION, scanTuneStation); 2505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_STATION_NUM, result[1]); 2506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_SCAN, isScan); 2507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Only notify the newest request activity 2508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyCurrentActivityStateChanged(bundle); 2509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsScanning = false; 2510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // audio focus changed 2513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_AUDIOFOCUS_CHANGED: 2514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int focusState = bundle.getInt(FmListener.KEY_AUDIOFOCUS_CHANGED); 2516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updateAudioFocus(focusState); 2517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SET_RDS_FINISHED: 2520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(bundle.getBoolean(OPTION)); 2522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SET_MUTE_FINISHED: 2525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(bundle.getBoolean(OPTION)); 2527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_ACTIVE_AF_FINISHED: 2530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang activeAf(); 2531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /********** recording **********/ 2534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_STARTRECORDING_FINISHED: 2535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRecording(); 2536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_STOPRECORDING_FINISHED: 2539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRecording(); 2540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_RECORD_MODE_CHANED: 2543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRecordingMode(bundle.getBoolean(OPTION)); 2545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SAVERECORDING_FINISHED: 2548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang saveRecording(bundle.getString(RECODING_FILE_NAME)); 2550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang default: 2553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * handle power down, execute power down and call back to activity. 2561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void handlePowerDown() { 2563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle; 2564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isPowerdown = powerDown(); 2565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(1); 2566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_POWERDOWN_FINISHED); 2567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * handle power up, execute power up and call back to activity. 2572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param bundle power up frequency 2574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void handlePowerUp(Bundle bundle) { 2576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isPowerUp = false; 2577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isSwitch = true; 2578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float curFrequency = bundle.getFloat(FM_FREQUENCY); 2579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isAntennaAvailable()) { 2581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "handlePowerUp, earphone is not ready"); 2582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(2); 2583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_SWITCH_ANNTENNA); 2584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANNTENNA, false); 2585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 2587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (powerUp(curFrequency)) { 2589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isFirstTimePlayFm(mContext)) { 2590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isPowerUp = firstPlaying(curFrequency); 2591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmUtils.setIsFirstTimePlayFm(mContext); 2592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isPowerUp = playFrequency(curFrequency); 2594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPausedByTransientLossOfFocus = false; 2596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(2); 2598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_POWERUP_FINISHED); 2599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_TUNE_TO_STATION, mCurrentStation); 2600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * check FM is foreground or background 2605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isActivityForeground() { 2607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mIsFmMainForeground || mIsFmFavoriteForground); 2608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * mark FmMainActivity is foreground or not 2612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isForeground 2613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setFmMainActivityForeground(boolean isForeground) { 2615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsFmMainForeground = isForeground; 2616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * mark FmFavoriteActivity activity is foreground or not 2620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isForeground 2621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setFmFavoriteForeground(boolean isForeground) { 2623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsFmFavoriteForground = isForeground; 2624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get the recording sdcard path when staring record 2628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return sdcard path like "/storage/sdcard0" 2630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static String getRecordingSdcard() { 2632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return sRecordingSdcard; 2633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * The listener interface for exit 2637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public interface OnExitListener { 2639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * When Service finish, should notify FmFavoriteActivity to finish 2641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang void onExit(); 2643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Register the listener for exit 2647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param listener The listener want to know the exit event 2649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static void registerExitListener(OnExitListener listener) { 2651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sExitListener = listener; 2652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Unregister the listener for exit 2656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param listener The listener want to know the exit event 2658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static void unregisterExitListener(OnExitListener listener) { 2660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sExitListener = null; 2661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get the latest recording name the show name in save dialog but saved in 2665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * service 2666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return The latest recording name or null for not modified 2668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public String getModifiedRecordingName() { 2670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mModifiedRecordingName; 2671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set the latest recording name if modify the default name 2675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param name The latest recording name or null for not modified 2677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setModifiedRecordingName(String name) { 2679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mModifiedRecordingName = name; 2680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 2683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onTaskRemoved(Intent rootIntent) { 2684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang exitFm(); 2685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onTaskRemoved(rootIntent); 2686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2688a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void volumeUp() { 2689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPatchVolume < 0) { 2690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPatchVolume += mPatchVolumeStep; 2691a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2692a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang adjustAudioGain(mPatchVolume); 2693a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2694a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2695a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void volumeDown() { 2696a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPatchVolume > -4500) { 2697a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPatchVolume -= mPatchVolumeStep; 2698a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2699a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang adjustAudioGain(mPatchVolume); 2700a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2701a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2702a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean firstPlaying(float frequency) { 2703a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 2704a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "firstPlaying, FM is not powered up"); 2705a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 2706a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2707a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isSeekTune = false; 2708a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float seekStation = FmNative.seek(frequency, false); 2709a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int station = FmUtils.computeStation(seekStation); 2710a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(station)) { 2711a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isSeekTune = FmNative.tune(seekStation); 2712a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isSeekTune) { 2713a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang playFrequency(seekStation); 2714a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2715a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2716a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if tune fail, pass current station to update ui 2717a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isSeekTune) { 2718a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang seekStation = FmUtils.computeFrequency(mCurrentStation); 2719a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2720a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return isSeekTune; 2721a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2722a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2723a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2724a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set the mIsDistanceExceed 2725a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param exceed true is exceed, false is not exceed 2726a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2727a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setDistanceExceed(boolean exceed) { 2728a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsDistanceExceed = exceed; 2729a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2730a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2731a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2732a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set notification class name 2733a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param clsName The target class name of activity 2734a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2735a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setNotificationClsName(String clsName) { 2736a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mTargetClassName = clsName; 2737a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2738a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang} 2739