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