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(); 39344c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang } 39444c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang if (mAudioTrack != null) { 39544c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang mAudioTrack.stop(); 39644c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang } 39744c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang initAudioRecordSink(); 3987c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang 399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsRender = true; 400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRenderLock) { 401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderLock.notify(); 402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void stopRender() { 4064b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang Log.d(TAG, "stopRender"); 407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsRender = false; 408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void createRenderThread() { 411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mRenderThread == null) { 412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderThread = new RenderThread(); 413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderThread.start(); 414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void exitRenderThread() { 418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderThread.interrupt(); 420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderThread = null; 421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private Thread mRenderThread = null; 424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private AudioRecord mAudioRecord = null; 425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private AudioTrack mAudioTrack = null; 426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int SAMPLE_RATE = 44100; 427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_CONFIGURATION_STEREO; 428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; 429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static final int RECORD_BUF_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE, 430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang CHANNEL_CONFIG, AUDIO_FORMAT); 431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean mIsRender = false; 432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePort mAudioSource = null; 434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePort mAudioSink = null; 435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isRendering() { 437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsRender; 438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void startAudioTrack() { 441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) { 442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.play(); 445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void stopAudioTrack() { 449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { 450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.stop(); 451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang class RenderThread extends Thread { 4555aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang private int mCurrentFrame = 0; 4565aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang private boolean isAudioFrameNeedIgnore() { 45787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang return mCurrentFrame < AUDIO_FRAMES_TO_IGNORE_COUNT; 4585aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang } 4595aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang 460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void run() { 462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang try { 463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang byte[] buffer = new byte[RECORD_BUF_SIZE]; 464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang while (!Thread.interrupted()) { 4655aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang if (isRender()) { 46687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // Speaker mode or BT a2dp mode will come here and keep reading and writing. 46787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // If we want FM sound output from speaker or BT a2dp, we must record data 46887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // to AudioRecrd and write data to AudioTrack. 4695aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) { 4705aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang mAudioRecord.startRecording(); 4715aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang } 47287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang 473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) { 474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.play(); 475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int size = mAudioRecord.read(buffer, 0, RECORD_BUF_SIZE); 4775aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang // check whether need to ignore first 3 frames audio data from AudioRecord 4785aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang // to avoid pop noise. 4795aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang if (isAudioFrameNeedIgnore()) { 4805aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang mCurrentFrame += 1; 4815aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang continue ; 4825aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang } 4837c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang if (size <= 0) { 4847c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang Log.e(TAG, "RenderThread read data from AudioRecord " 4857c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang + "error size: " + size); 4867c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang continue; 4877c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang } 488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang byte[] tmpBuf = new byte[size]; 489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang System.arraycopy(buffer, 0, tmpBuf, 0, size); 49087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // Check again to avoid noises, because mIsRender may be changed 49187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // while AudioRecord is reading. 4925aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang if (isRender()) { 4935aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang mAudioTrack.write(tmpBuf, 0, tmpBuf.length); 494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 4955aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang } else { 49687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // Earphone mode will come here and wait. 4975aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang mCurrentFrame = 0; 49887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang 499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { 500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.stop(); 501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 5035aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { 5045aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang mAudioRecord.stop(); 5055aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang } 5065aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang 507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRenderLock) { 508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRenderLock.wait(); 509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } catch (InterruptedException e) { 513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "RenderThread.run, thread is interrupted, need exit thread"); 514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } finally { 515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { 516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioRecord.stop(); 517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { 519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack.stop(); 520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // A2dp or speaker mode should render 526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isRender() { 527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mIsRender && (mPowerStatus == POWER_UP) && mIsAudioFocusHeld); 528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isSpeakerPhoneOn() { 531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mForcedUseForMedia == AudioSystem.FORCE_SPEAKER); 532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * open FM device, should be call before power up 536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if FM device open, false FM device not open 538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean openDevice() { 540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsDeviceOpen) { 541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsDeviceOpen = FmNative.openDev(); 542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsDeviceOpen; 544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * close FM device 548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if close FM device success, false close FM device failed 550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean closeDevice() { 552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isDeviceClose = false; 553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsDeviceOpen) { 554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isDeviceClose = FmNative.closeDev(); 555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsDeviceOpen = !isDeviceClose; 556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // quit looper 558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.getLooper().quit(); 559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return isDeviceClose; 560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * get FM device opened or not 564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true FM device opened, false FM device closed 566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isDeviceOpen() { 568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsDeviceOpen; 569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * power up FM, and make FM voice output from earphone 573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param frequency 575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void powerUpAsync(float frequency) { 577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED); 579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED); 580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FM_FREQUENCY, frequency); 582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_POWERUP_FINISHED); 583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean powerUp(float frequency) { 588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mWakeLock.isHeld()) { 592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.acquire(); 593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!requestAudioFocus()) { 595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // activity used for update powerdown menu 596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = POWER_DOWN; 597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = DURING_POWER_UP; 601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if device open fail when chip reset, it need open device again before 603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // power up 604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsDeviceOpen) { 605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang openDevice(); 606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!FmNative.powerUp(frequency)) { 609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = POWER_DOWN; 610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = POWER_UP; 613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // need mute after power up 614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mPowerStatus == POWER_UP); 617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean playFrequency(float frequency) { 620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mCurrentStation = FmUtils.computeStation(frequency); 621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.setCurrentStation(mContext, mCurrentStation); 622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Add notification to the title bar. 623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updatePlayingNotification(); 624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Start the RDS thread if RDS is supported. 626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRdsThread(); 628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mWakeLock.isHeld()) { 631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.acquire(); 632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsSpeakerUsed != isSpeakerPhoneOn()) { 634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setForceUse(mIsSpeakerUsed); 635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mRecordState != FmRecorder.STATE_PLAYBACK) { 637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(true); 638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(true); 641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (mPowerStatus == POWER_UP); 644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * power down FM 648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void powerDownAsync() { 650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if power down Fm, should remove message first. 651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // not remove all messages, because such as recorder message need 652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // to execute after or before power down 653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED); 654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED); 655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_TUNE_FINISHED); 656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED); 657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED); 658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_POWERDOWN_FINISHED); 659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Power down FM 663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if power down success 665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean powerDown() { 667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_DOWN) { 668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(false); 673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(false); 674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!FmNative.powerDown(0)) { 676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRdsThread(); 679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mWakeLock.isHeld()) { 682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.release(); 683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Remove the notification in the title bar. 685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang removeNotification(); 686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 688a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // activity used for update powerdown menu 689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPowerStatus = POWER_DOWN; 690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 691a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 692a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRdsThread(); 693a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 694a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 695a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mWakeLock.isHeld()) { 696a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.release(); 697a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 698a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 699a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Remove the notification in the title bar. 700a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang removeNotification(); 701a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 702a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 703a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 704a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int getPowerStatus() { 705a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mPowerStatus; 706a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 707a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 708a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 709a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Tune to a station 710a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 711a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param frequency The frequency to tune 712a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 713a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true, success; false, fail. 714a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 715a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void tuneStationAsync(float frequency) { 716a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_TUNE_FINISHED); 717a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 718a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 719a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FM_FREQUENCY, frequency); 720a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_TUNE_FINISHED); 721a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 722a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 723a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 724a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 725a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean tuneStation(float frequency) { 726a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 727a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(false); 728a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean bRet = FmNative.tune(frequency); 729a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (bRet) { 730a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(true); 731a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mCurrentStation = FmUtils.computeStation(frequency); 732a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.setCurrentStation(mContext, mCurrentStation); 733a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updatePlayingNotification(); 734a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 735a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 736a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return bRet; 737a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 738a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 739a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if earphone is not insert, not power up 740a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isAntennaAvailable()) { 741a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 742a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 743a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 744a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if not power up yet, should powerup first 745a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean tune = false; 746a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 747a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (powerUp(frequency)) { 748a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang tune = playFrequency(frequency); 749a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 750a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 751a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return tune; 752a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 753a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 754a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 755a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Seek station according frequency and direction 756a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 757a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param frequency start frequency(100KHZ, 87.5) 758a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isUp direction(true, next station; false, previous station) 759a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 760a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return the frequency after seek 761a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 762a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void seekStationAsync(float frequency, boolean isUp) { 763a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED); 764a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 2; 765a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 766a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FM_FREQUENCY, frequency); 767a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(OPTION, isUp); 768a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SEEK_FINISHED); 769a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 770a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 771a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 772a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 773a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private float seekStation(float frequency, boolean isUp) { 774a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 775a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return -1; 776a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 777a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 778a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(false); 779a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsNativeSeeking = true; 780a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float fRet = FmNative.seek(frequency, isUp); 781a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsNativeSeeking = false; 782a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // make mIsStopScanCalled false, avoid stop scan make this true, 783a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // when start scan, it will return null. 784a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsStopScanCalled = false; 785a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return fRet; 786a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 787a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 788a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 789a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Scan stations 790a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 791a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void startScanAsync() { 792a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED); 793a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_SCAN_FINISHED); 794a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 795a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 796a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int[] startScan() { 797a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int[] stations = null; 798a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 799a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(false); 800a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 801a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang short[] stationsInShort = null; 802a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsStopScanCalled) { 803a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsNativeScanning = true; 804a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationsInShort = FmNative.autoScan(); 805a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsNativeScanning = false; 806a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 807a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 808a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(true); 809a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsStopScanCalled) { 810a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Received a message to power down FM, or interrupted by a phone 811a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // call. Do not return any stations. stationsInShort = null; 812a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if cancel scan, return invalid station -100 813a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationsInShort = new short[] { 814a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang -100 815a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 816a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsStopScanCalled = false; 817a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 818a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 819a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != stationsInShort) { 820a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int size = stationsInShort.length; 821a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stations = new int[size]; 822a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int i = 0; i < size; i++) { 823a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stations[i] = stationsInShort[i]; 824a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 825a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 826a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return stations; 827a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 828a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 829a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 830a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check FM Radio is in scan progress or not 831a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 832a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return if in scan progress return true, otherwise return false. 833a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 834a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isScanning() { 835a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsScanning; 836a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 837a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 838a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 839a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Stop scan progress 840a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 841a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if can stop scan, otherwise return false. 842a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 843a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean stopScan() { 844a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 845a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 846a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 847a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 848a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean bRet = false; 849a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED); 850a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED); 851a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsNativeScanning || mIsNativeSeeking) { 852a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsStopScanCalled = true; 853a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bRet = FmNative.stopScan(); 854a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 855a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return bRet; 856a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 857a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 858a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 859a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check FM is in seek progress or not 860a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 861a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if in seek progress, otherwise return false. 862a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 863a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isSeeking() { 864a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsNativeSeeking; 865a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 866a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 867a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 868a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set RDS 869a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 870a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param on true, enable RDS; false, disable RDS. 871a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 872a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setRdsAsync(boolean on) { 873a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 874a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SET_RDS_FINISHED); 875a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 876a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(OPTION, on); 877a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SET_RDS_FINISHED); 878a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 879a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 880a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 881a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 882a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int setRds(boolean on) { 883a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 884a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return -1; 885a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 886a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int ret = -1; 887a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 888a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ret = FmNative.setRds(on); 889a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 890a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return ret; 891a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 892a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 893a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 894a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get PS information 895a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 896a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return PS information 897a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 898a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public String getPs() { 899a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mPsString; 900a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 901a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 902a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 903a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get RT information 904a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 905a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return RT information 906a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 907a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public String getRtText() { 908a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mRtTextString; 909a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 910a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 911a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 912a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get AF frequency 913a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 914a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return AF frequency 915a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 916a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void activeAfAsync() { 917a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_ACTIVE_AF_FINISHED); 918a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_ACTIVE_AF_FINISHED); 919a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 920a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 921a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int activeAf() { 922a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 923a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "activeAf, FM is not powered up"); 924a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return -1; 925a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 926a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 927a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int frequency = FmNative.activeAf(); 928a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return frequency; 929a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 930a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 931a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 932a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Mute or unmute FM voice 933a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 934a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param mute true for mute, false for unmute 935a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 936a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (true, success; false, failed) 937a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 938a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setMuteAsync(boolean mute) { 939a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SET_MUTE_FINISHED); 940a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 941a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 942a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(OPTION, mute); 943a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SET_MUTE_FINISHED); 944a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 945a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 946a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 947a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 948a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 949a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Mute or unmute FM voice 950a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 951a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param mute true for mute, false for unmute 952a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 953a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (1, success; other, failed) 954a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 955a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int setMute(boolean mute) { 956a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 957a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "setMute, FM is not powered up"); 958a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return -1; 959a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 960a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int iRet = FmNative.setMute(mute); 961a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsMuted = mute; 962a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return iRet; 963a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 964a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 965a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 966a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check the latest status is mute or not 967a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 968a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (true, mute; false, unmute) 969a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 970a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isMuted() { 971a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsMuted; 972a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 973a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 974a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 975a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check whether RDS is support in driver 976a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 977a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (true, support; false, not support) 978a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 979a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isRdsSupported() { 980a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isRdsSupported = (FmNative.isRdsSupport() == 1); 981a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return isRdsSupported; 982a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 983a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 984a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 985a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check whether speaker used or not 986a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 987a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if use speaker, otherwise return false 988a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 989a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isSpeakerUsed() { 990a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsSpeakerUsed; 991a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 992a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 993a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 994a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Initial service and current station 995a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 996a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param iCurrentStation current station frequency 997a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 998a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void initService(int iCurrentStation) { 999a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsServiceInited = true; 1000a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mCurrentStation = iCurrentStation; 1001a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1002a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1003a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1004a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check service is initialed or not 1005a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1006a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true if initialed, otherwise return false 1007a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1008a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isServiceInited() { 1009a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsServiceInited; 1010a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1011a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1012a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1013a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get FM service current station frequency 1014a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1015a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return Current station frequency 1016a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1017a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int getFrequency() { 1018a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mCurrentStation; 1019a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1020a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1021a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1022a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set FM service station frequency 1023a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1024a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param station Current station 1025a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1026a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setFrequency(int station) { 1027a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mCurrentStation = station; 1028a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1029a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1030a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1031a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * resume FM audio 1032a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1033a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void resumeFmAudio() { 1034a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // If not check mIsAudioFocusHeld && power up, when scan canceled, 1035a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // this will be resume first, then execute power down. it will cause 1036a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // nosise. 1037a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsAudioFocusHeld && (mPowerStatus == POWER_UP)) { 1038a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(true); 1039a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1040a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1041a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1042a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1043a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Switch antenna There are two types of antenna(long and short) If long 1044a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * antenna(most is this type), must plug in earphone as antenna to receive 1045a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * FM. If short antenna, means there is a short antenna if phone already, 1046a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * can receive FM without earphone. 1047a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1048a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param antenna antenna (0, long antenna, 1 short antenna) 1049a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1050a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (0, success; 1 failed; 2 not support) 1051a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1052a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void switchAntennaAsync(int antenna) { 1053a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 10546e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SWITCH_ANTENNA); 1055a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1056a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 10576e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang bundle.putInt(FmListener.SWITCH_ANTENNA_VALUE, antenna); 10586e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SWITCH_ANTENNA); 1059a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 1060a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 1061a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1062a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1063a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1064a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Need native support whether antenna support interface. 1065a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1066a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param antenna antenna (0, long antenna, 1 short antenna) 1067a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1068a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return (0, success; 1 failed; 2 not support) 1069a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1070a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int switchAntenna(int antenna) { 1071a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if fm not powerup, switchAntenna will flag whether has earphone 1072a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int ret = FmNative.switchAntenna(antenna); 1073a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return ret; 1074a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1075a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1076a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1077a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Start recording 1078a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1079a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void startRecordingAsync() { 1080a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_STARTRECORDING_FINISHED); 1081a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_STARTRECORDING_FINISHED); 1082a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1083a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1084a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void startRecording() { 1085a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sRecordingSdcard = FmUtils.getDefaultStoragePath(); 1086a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sRecordingSdcard == null || sRecordingSdcard.isEmpty()) { 1087a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRecording, may be no sdcard"); 1088a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang onRecorderError(FmRecorder.ERROR_SDCARD_NOT_PRESENT); 1089a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1090a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1091a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1092a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder == null) { 1093a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder = new FmRecorder(); 1094a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.registerRecorderStateListener(FmService.this); 1095a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1096a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1097a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isSdcardReady(sRecordingSdcard)) { 1098a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.startRecording(mContext); 1099a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang onRecorderError(FmRecorder.ERROR_SDCARD_NOT_PRESENT); 1101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isSdcardReady(String sdcardPath) { 1105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mSdcardStateMap.isEmpty()) { 1106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mSdcardStateMap.get(sdcardPath) != null && !mSdcardStateMap.get(sdcardPath)) { 1107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "isSdcardReady, return false"); 1108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 1109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 1112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * stop recording 1116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void stopRecordingAsync() { 1118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_STOPRECORDING_FINISHED); 1119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_STOPRECORDING_FINISHED); 1120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean stopRecording() { 1123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder == null) { 1124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.e(TAG, "stopRecording, called without a valid recorder!!"); 1125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 1126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mStopRecordingLock) { 1128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.stopRecording(); 1129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 1131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Save recording file according name or discard recording file if name is 1135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * null 1136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param newName New recording file name 1138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void saveRecordingAsync(String newName) { 1140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_SAVERECORDING_FINISHED); 1141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 1142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 1143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putString(RECODING_FILE_NAME, newName); 1144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SAVERECORDING_FINISHED); 1145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 1146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 1147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void saveRecording(String newName) { 1150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 1151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (newName != null) { 1152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.saveRecording(FmService.this, newName); 1153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.discardRecording(); 1156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get record time 1161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return Record time 1163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public long getRecordTime() { 1165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 1166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mFmRecorder.getRecordTime(); 1167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return 0; 1169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set recording mode 1173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isRecording true, enter recoding mode; false, exit recording mode 1175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setRecordingModeAsync(boolean isRecording) { 1177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_RECORD_MODE_CHANED); 1178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 1179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 1180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(OPTION, isRecording); 1181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_RECORD_MODE_CHANED); 1182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 1183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 1184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void setRecordingMode(boolean isRecording) { 1187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsInRecordingMode = isRecording; 1188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 1189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isRecording) { 1190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder.getState() != FmRecorder.STATE_IDLE) { 1191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.stopRecording(); 1192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang resumeFmAudio(); 1194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 1195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // reset recorder to unused status 1198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.resetRecorder(); 1199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get current recording mode 1204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return if in recording mode return true, otherwise return false; 1206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean getRecordingMode() { 1208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsInRecordingMode; 1209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get record state 1213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return record state 1215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int getRecorderState() { 1217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mFmRecorder) { 1218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mFmRecorder.getState(); 1219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return FmRecorder.STATE_INVALID; 1221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get recording file name 1225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return recording file name 1227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public String getRecordingName() { 1229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mFmRecorder) { 1230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mFmRecorder.getRecordFileName(); 1231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return null; 1233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onCreate() { 1237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onCreate(); 1238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mContext = getApplicationContext(); 1239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 1240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 1241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); 1242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 1243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mWakeLock.setReferenceCounted(false); 1244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sRecordingSdcard = FmUtils.getDefaultStoragePath(); 1245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerFmBroadcastReceiver(); 1247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerSdcardReceiver(); 1248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerAudioPortUpdateListener(); 1249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang HandlerThread handlerThread = new HandlerThread("FmRadioServiceThread"); 1251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlerThread.start(); 1252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler = new FmRadioServiceHandler(handlerThread.getLooper()); 1253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang openDevice(); 1255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // set speaker to default status, avoid setting->clear data. 1256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setForceUse(mIsSpeakerUsed); 1257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang initAudioRecordSink(); 1259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang createRenderThread(); 1260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void registerAudioPortUpdateListener() { 1263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPortUpdateListener == null) { 1264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioPortUpdateListener = new FmOnAudioPortUpdateListener(); 1265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.registerAudioPortUpdateListener(mAudioPortUpdateListener); 1266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void unregisterAudioPortUpdateListener() { 1270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPortUpdateListener != null) { 1271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.unregisterAudioPortUpdateListener(mAudioPortUpdateListener); 1272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioPortUpdateListener = null; 1273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 127687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // This function may be called in different threads. 127787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // Need to add "synchronized" to make sure mAudioRecord and mAudioTrack are the newest. 127887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // Thread 1: onCreate() or startRender() 127987171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // Thread 2: onAudioPatchListUpdate() or startRender() 128087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang private synchronized void initAudioRecordSink() { 1281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.FM_TUNER, 1282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, RECORD_BUF_SIZE); 1283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 1284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, RECORD_BUF_SIZE, AudioTrack.MODE_STREAM); 1285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void createAudioPatch() { 12884b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang Log.d(TAG, "createAudioPatch"); 1289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch != null) { 1290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "createAudioPatch, mAudioPatch is not null, return"); 1291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSource = null; 1295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSink = null; 1296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); 1297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPorts(ports); 1298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (AudioPort port : ports) { 1299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (port instanceof AudioDevicePort) { 1300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int type = ((AudioDevicePort) port).type(); 1301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String name = AudioSystem.getOutputDeviceName(type); 1302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (type == AudioSystem.DEVICE_IN_FM_TUNER) { 1303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSource = (AudioDevicePort) port; 1304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (type == AudioSystem.DEVICE_OUT_WIRED_HEADSET || 1305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang type == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) { 1306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSink = (AudioDevicePort) port; 1307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioSource != null && mAudioSink != null) { 1311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePortConfig sourceConfig = (AudioDevicePortConfig) mAudioSource 1312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang .activeConfig(); 1313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioDevicePortConfig sinkConfig = (AudioDevicePortConfig) mAudioSink.activeConfig(); 1314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPatch[] audioPatchArray = new AudioPatch[] {null}; 1315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.createAudioPatch(audioPatchArray, 1316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new AudioPortConfig[] {sourceConfig}, 1317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new AudioPortConfig[] {sinkConfig}); 1318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioPatch = audioPatchArray[0]; 1319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private FmOnAudioPortUpdateListener mAudioPortUpdateListener = null; 1323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private class FmOnAudioPortUpdateListener implements OnAudioPortUpdateListener { 1325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Callback method called upon audio port list update. 1327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param portList the updated list of audio ports 1328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onAudioPortListUpdate(AudioPort[] portList) { 13314b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang // Ingore audio port update 1332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Callback method called upon audio patch list update. 1336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param patchList the updated list of audio patches 1338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onAudioPatchListUpdate(AudioPatch[] patchList) { 1341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 134287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang Log.d(TAG, "onAudioPatchListUpdate, not power up"); 1343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsAudioFocusHeld) { 134787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang Log.d(TAG, "onAudioPatchListUpdate no audio focus"); 1348a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch != null) { 1352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 1353a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 135487171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // When BT or WFD is connected, native will remove the patch (mixer -> device). 135587171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // Need to recreate AudioRecord and AudioTrack for this case. 135687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang if (isPatchMixerToDeviceRemoved(patches)) { 135787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang Log.d(TAG, "onAudioPatchListUpdate reinit for BT or WFD connected"); 135887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang initAudioRecordSink(); 135987171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang startRender(); 136087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang return; 136187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang } 1362a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isPatchMixerToEarphone(patches)) { 1363a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1364a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1365a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseAudioPatch(); 1366a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRender(); 1367a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (mIsRender) { 1369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 1370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 1371a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isPatchMixerToEarphone(patches)) { 1372a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopAudioTrack(); 1373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang createAudioPatch(); 1375a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1376a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1377a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1378a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1380a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Callback method called when the mediaserver dies 1381a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1382a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1383a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onServiceDied() { 1384a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang enableFmAudio(false); 1385a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1387a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1388a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void releaseAudioPatch() { 1389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch != null) { 13904b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang Log.d(TAG, "releaseAudioPatch"); 1391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.releaseAudioPatch(mAudioPatch); 1392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioPatch = null; 1393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSource = null; 1395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioSink = null; 1396a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1397a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void registerFmBroadcastReceiver() { 1399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang IntentFilter filter = new IntentFilter(); 1400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(SOUND_POWER_DOWN_MSG); 1401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_SHUTDOWN); 1402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_SCREEN_ON); 1403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_SCREEN_OFF); 1404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_HEADSET_PLUG); 1405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mBroadcastReceiver = new FmServiceBroadcastReceiver(); 1406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerReceiver(mBroadcastReceiver, filter); 1407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void unregisterFmBroadcastReceiver() { 1410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mBroadcastReceiver) { 1411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterReceiver(mBroadcastReceiver); 1412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mBroadcastReceiver = null; 1413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onDestroy() { 1418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.setParameters("AudioFmPreStop=1"); 1419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 1420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // stop rds first, avoid blocking other native method 1421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isRdsSupported()) { 1422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRdsThread(); 1423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterFmBroadcastReceiver(); 1425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterSdcardListener(); 1426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang abandonAudioFocus(); 1427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang exitFm(); 1428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mFmRecorder) { 1429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder = null; 1430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang exitRenderThread(); 1432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseAudioPatch(); 1433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterAudioPortUpdateListener(); 1434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onDestroy(); 1435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Exit FMRadio application 1439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void exitFm() { 1441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsAudioFocusHeld = false; 1442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Stop FM recorder if it is working 1443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mFmRecorder) { 1444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mStopRecordingLock) { 1445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int fmState = mFmRecorder.getState(); 1446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmRecorder.STATE_RECORDING == fmState) { 1447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmRecorder.stopRecording(); 1448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // When exit, we set the audio path back to earphone. 1453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsNativeScanning || mIsNativeSeeking) { 1454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopScan(); 1455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeCallbacksAndMessages(null); 1458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_FM_EXIT); 1459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_FM_EXIT); 1460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onConfigurationChanged(Configuration newConfig) { 1464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onConfigurationChanged(newConfig); 1465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Change the notification string. 1466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 1467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang showPlayingNotification(); 1468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public int onStartCommand(Intent intent, int flags, int startId) { 1473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int ret = super.onStartCommand(intent, flags, startId); 1474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (intent != null) { 1476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String action = intent.getAction(); 1477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FM_SEEK_PREVIOUS.equals(action)) { 1478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang seekStationAsync(FmUtils.computeFrequency(mCurrentStation), false); 1479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (FM_SEEK_NEXT.equals(action)) { 1480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang seekStationAsync(FmUtils.computeFrequency(mCurrentStation), true); 1481a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (FM_TURN_OFF.equals(action)) { 1482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang powerDownAsync(); 1483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return START_NOT_STICKY; 1486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Start RDS thread to update RDS information 1490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void startRdsThread() { 1492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsRdsThreadExit = false; 1493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mRdsThread) { 1494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRdsThread = new Thread() { 1497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void run() { 1498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang while (true) { 1499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsRdsThreadExit) { 1500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 1501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int iRdsEvents = FmNative.readRds(); 1504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (iRdsEvents != 0) { 1505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRdsThread, is rds events: " + iRdsEvents); 1506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (RDS_EVENT_PROGRAMNAME == (RDS_EVENT_PROGRAMNAME & iRdsEvents)) { 1509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang byte[] bytePS = FmNative.getPs(); 1510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != bytePS) { 1511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String ps = new String(bytePS).trim(); 1512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mPsString.equals(ps)) { 1513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updatePlayingNotification(); 1514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ContentValues values = null; 1516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmStation.isStationExist(mContext, mCurrentStation)) { 1517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values = new ContentValues(1); 1518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.PROGRAM_SERVICE, ps); 1519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.updateStationToDb(mContext, mCurrentStation, values); 1520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values = new ContentValues(2); 1522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.FREQUENCY, mCurrentStation); 1523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.PROGRAM_SERVICE, ps); 1524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.insertStationToDb(mContext, values); 1525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setPs(ps); 1527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (RDS_EVENT_LAST_RADIOTEXT == (RDS_EVENT_LAST_RADIOTEXT & iRdsEvents)) { 1531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang byte[] byteLRText = FmNative.getLrText(); 1532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != byteLRText) { 1533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String rds = new String(byteLRText).trim(); 15349dd97ba8b788592899438fd2c7d4ab88bfe3dccdBenson Huang if (!mRtTextString.equals(rds)) { 15359dd97ba8b788592899438fd2c7d4ab88bfe3dccdBenson Huang updatePlayingNotification(); 15369dd97ba8b788592899438fd2c7d4ab88bfe3dccdBenson Huang } 1537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setLRText(rds); 1538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ContentValues values = null; 1539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmStation.isStationExist(mContext, mCurrentStation)) { 1540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values = new ContentValues(1); 1541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.RADIO_TEXT, rds); 1542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.updateStationToDb(mContext, mCurrentStation, values); 1543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values = new ContentValues(2); 1545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.FREQUENCY, mCurrentStation); 1546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang values.put(Station.RADIO_TEXT, rds); 1547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.insertStationToDb(mContext, values); 1548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (RDS_EVENT_AF == (RDS_EVENT_AF & iRdsEvents)) { 1553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /* 1554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * add for rds AF 1555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsScanning || mIsSeeking) { 1557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRdsThread, seek or scan going, no need to tune here"); 1558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (mPowerStatus == POWER_DOWN) { 1559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRdsThread, fm is power down, do nothing."); 1560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int iFreq = FmNative.activeAf(); 1562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(iFreq)) { 1563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if the new frequency is not equal to current 1564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // frequency. 1565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mCurrentStation != iFreq) { 1566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mIsScanning && !mIsSeeking) { 1567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "startRdsThread, seek or scan not going," 1568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang + "need to tune here"); 1569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang tuneStationAsync(FmUtils.computeFrequency(iFreq)); 1570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Do not handle other events. 1576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Sleep 500ms to reduce inquiry frequency 1577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang try { 1578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int hundredMillisecond = 500; 1579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Thread.sleep(hundredMillisecond); 1580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } catch (InterruptedException e) { 1581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang e.printStackTrace(); 1582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 1586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRdsThread.start(); 1587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Stop RDS thread to stop listen station RDS change 1591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void stopRdsThread() { 1593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mRdsThread) { 1594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Must call closedev after stopRDSThread. 1595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsRdsThreadExit = true; 1596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRdsThread = null; 1597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set PS information 1602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param ps The ps information 1604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void setPs(String ps) { 1606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (0 != mPsString.compareTo(ps)) { 1607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPsString = ps; 1608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(3); 1609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_PS_CHANGED); 1610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putString(FmListener.KEY_PS_INFO, mPsString); 1611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 1612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } // else New PS is the same as current 1613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set RT information 1617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param lrtText The RT information 1619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void setLRText(String lrtText) { 1621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (0 != mRtTextString.compareTo(lrtText)) { 1622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRtTextString = lrtText; 1623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(3); 1624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RT_CHANGED); 1625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putString(FmListener.KEY_RT_INFO, mRtTextString); 1626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 1627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } // else New RT is the same as current 1628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Open or close FM Radio audio 1632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param enable true, open FM audio; false, close FM audio; 1634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void enableFmAudio(boolean enable) { 1636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (enable) { 1637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((mPowerStatus != POWER_UP) || !mIsAudioFocusHeld) { 1638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "enableFmAudio, current not available return.mIsAudioFocusHeld:" 1639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang + mIsAudioFocusHeld); 1640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startAudioTrack(); 1644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 1645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.listAudioPatches(patches); 1646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mAudioPatch == null) { 1647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isPatchMixerToEarphone(patches)) { 1648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopAudioTrack(); 1649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang createAudioPatch(); 1651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRender(); 1653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang releaseAudioPatch(); 1657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRender(); 1658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Make sure patches count will not be 0 1662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isPatchMixerToEarphone(ArrayList<AudioPatch> patches) { 1663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int deviceCount = 0; 1664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int deviceEarphoneCount = 0; 1665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (AudioPatch patch : patches) { 1666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPortConfig[] sources = patch.sources(); 1667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPortConfig[] sinks = patch.sinks(); 1668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPortConfig sourceConfig = sources[0]; 1669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPortConfig sinkConfig = sinks[0]; 1670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPort sourcePort = sourceConfig.port(); 1671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioPort sinkPort = sinkConfig.port(); 16727c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang Log.d(TAG, "isPatchMixerToEarphone " + sourcePort + " ====> " + sinkPort); 1673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sourcePort instanceof AudioMixPort && sinkPort instanceof AudioDevicePort) { 1674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang deviceCount++; 1675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int type = ((AudioDevicePort) sinkPort).type(); 1676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (type == AudioSystem.DEVICE_OUT_WIRED_HEADSET || 1677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang type == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) { 1678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang deviceEarphoneCount++; 1679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (deviceEarphoneCount == 1 && deviceCount == deviceEarphoneCount) { 1683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 1684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 1686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 168887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // Check whether the patch (mixer -> device) is removed by native. 168987171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang // If no patch (mixer -> device), return true. 169087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang private boolean isPatchMixerToDeviceRemoved(ArrayList<AudioPatch> patches) { 169187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang boolean noMixerToDevice = true; 169287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang for (AudioPatch patch : patches) { 169387171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang AudioPortConfig[] sources = patch.sources(); 169487171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang AudioPortConfig[] sinks = patch.sinks(); 169587171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang AudioPortConfig sourceConfig = sources[0]; 169687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang AudioPortConfig sinkConfig = sinks[0]; 169787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang AudioPort sourcePort = sourceConfig.port(); 169887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang AudioPort sinkPort = sinkConfig.port(); 169987171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang 170087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang if (sourcePort instanceof AudioMixPort && sinkPort instanceof AudioDevicePort) { 170187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang noMixerToDevice = false; 170287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang break; 170387171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang } 170487171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang } 170587171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang return noMixerToDevice; 170687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang } 170787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang 1708a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1709a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Show notification 1710a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1711a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void showPlayingNotification() { 1712a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isActivityForeground() || mIsScanning 1713a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang || (getRecorderState() == FmRecorder.STATE_RECORDING)) { 1714a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "showPlayingNotification, do not show main notification."); 1715a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 1716a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1717a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String stationName = ""; 171810f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang String radioText = ""; 1719a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ContentResolver resolver = mContext.getContentResolver(); 1720a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Cursor cursor = null; 1721a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang try { 1722a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang cursor = resolver.query( 1723a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Station.CONTENT_URI, 1724a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.COLUMNS, 1725a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Station.FREQUENCY + "=?", 1726a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new String[] { String.valueOf(mCurrentStation) }, 1727a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang null); 1728a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (cursor != null && cursor.moveToFirst()) { 1729a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // If the station name is not exist, show program service(PS) instead 1730a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationName = cursor.getString(cursor.getColumnIndex(Station.STATION_NAME)); 1731a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (TextUtils.isEmpty(stationName)) { 1732a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationName = cursor.getString(cursor.getColumnIndex(Station.PROGRAM_SERVICE)); 1733a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 173410f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang radioText = cursor.getString(cursor.getColumnIndex(Station.RADIO_TEXT)); 1735a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1736a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1737a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "showPlayingNotification, cursor is null"); 1738a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1739a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } finally { 1740a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (cursor != null) { 1741a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang cursor.close(); 1742a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1743a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1744a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1745a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Intent aIntent = new Intent(Intent.ACTION_MAIN); 1746a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang aIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1747a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1748a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang aIntent.setClassName(getPackageName(), mTargetClassName); 1749a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang PendingIntent pAIntent = PendingIntent.getActivity(mContext, 0, aIntent, 0); 1750a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1751a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null == mNotificationBuilder) { 1752a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder = new Notification.Builder(mContext); 1753a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setSmallIcon(R.drawable.ic_launcher); 1754a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setShowWhen(false); 1755a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setAutoCancel(true); 1756a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1757a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Intent intent = new Intent(FM_SEEK_PREVIOUS); 1758a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent.setClass(mContext, FmService.class); 1759a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang PendingIntent pIntent = PendingIntent.getService(mContext, 0, intent, 0); 1760a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.addAction(R.drawable.btn_fm_prevstation, "", pIntent); 1761a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent = new Intent(FM_TURN_OFF); 1762a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent.setClass(mContext, FmService.class); 1763a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang pIntent = PendingIntent.getService(mContext, 0, intent, 0); 1764a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.addAction(R.drawable.btn_fm_rec_stop_enabled, "", pIntent); 1765a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent = new Intent(FM_SEEK_NEXT); 1766a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang intent.setClass(mContext, FmService.class); 1767a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang pIntent = PendingIntent.getService(mContext, 0, intent, 0); 1768a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.addAction(R.drawable.btn_fm_nextstation, "", pIntent); 1769a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1770a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setContentIntent(pAIntent); 1771a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bitmap largeIcon = FmUtils.createNotificationLargeIcon(mContext, 1772a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmUtils.formatStation(mCurrentStation)); 1773a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setLargeIcon(largeIcon); 1774a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Show FM Radio if empty 1775a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (TextUtils.isEmpty(stationName)) { 1776a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationName = getString(R.string.app_name); 1777a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1778a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mNotificationBuilder.setContentTitle(stationName); 177910f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang // If radio text is "" or null, we also need to update notification. 178010f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang mNotificationBuilder.setContentText(radioText); 178110f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang Log.d(TAG, "showPlayingNotification PS:" + stationName + ", RT:" + radioText); 1782a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1783a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Notification n = mNotificationBuilder.build(); 1784a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang n.flags &= ~Notification.FLAG_NO_CLEAR; 1785a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startForeground(NOTIFICATION_ID, n); 1786a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1787a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1788a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1789a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Show notification 1790a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1791a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void showRecordingNotification(Notification notification) { 1792a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startForeground(NOTIFICATION_ID, notification); 1793a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1794a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1795a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1796a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Remove notification 1797a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1798a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void removeNotification() { 1799a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopForeground(true); 1800a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1801a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1802a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1803a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Update notification 1804a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1805a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void updatePlayingNotification() { 1806a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 1807a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang showPlayingNotification(); 1808a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1809a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1810a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1811a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1812a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Register sdcard listener for record 1813a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1814a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void registerSdcardReceiver() { 1815a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mSdcardListener == null) { 1816a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSdcardListener = new SdcardListener(); 1817a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1818a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang IntentFilter filter = new IntentFilter(); 1819a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addDataScheme("file"); 1820a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_MEDIA_MOUNTED); 1821a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); 1822a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang filter.addAction(Intent.ACTION_MEDIA_EJECT); 1823a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang registerReceiver(mSdcardListener, filter); 1824a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1825a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1826a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void unregisterSdcardListener() { 1827a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != mSdcardListener) { 1828a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang unregisterReceiver(mSdcardListener); 1829a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1830a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1831a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1832a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void updateSdcardStateMap(Intent intent) { 1833a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String action = intent.getAction(); 1834a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String sdcardPath = null; 1835a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Uri mountPointUri = intent.getData(); 1836a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mountPointUri != null) { 1837a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sdcardPath = mountPointUri.getPath(); 1838a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sdcardPath != null) { 1839a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (Intent.ACTION_MEDIA_EJECT.equals(action)) { 1840a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSdcardStateMap.put(sdcardPath, false); 1841a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (Intent.ACTION_MEDIA_UNMOUNTED.equals(action)) { 1842a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSdcardStateMap.put(sdcardPath, false); 1843a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) { 1844a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mSdcardStateMap.put(sdcardPath, true); 1845a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1846a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1847a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1848a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1849a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1850a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1851a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Notify FM recorder state 1852a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1853a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param state The current FM recorder state 1854a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1855a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1856a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onRecorderStateChanged(int state) { 1857a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRecordState = state; 1858a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(2); 1859a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RECORDSTATE_CHANGED); 1860a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_RECORDING_STATE, state); 1861a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 1862a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1863a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1864a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1865a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Notify FM recorder error message 1866a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1867a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param error The recorder error type 1868a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1869a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1870a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onRecorderError(int error) { 1871a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if media server die, will not enable FM audio, and convert to 1872a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // ERROR_PLAYER_INATERNAL, call back to activity showing toast. 1873a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRecorderErrorType = error; 1874a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1875a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(2); 1876a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RECORDERROR); 1877a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_RECORDING_ERROR_TYPE, mRecorderErrorType); 1878a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 1879a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1880a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1881a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1882a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check and go next(play or show tips) after recorder file play 1883a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * back finish. 1884a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Two cases: 1885a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1. With headset -> play FM 1886a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2. Without headset -> show plug in earphone tips 1887a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1888a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void checkState() { 1889a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isHeadSetIn()) { 1890a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // with headset 1891a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 1892a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang resumeFmAudio(); 1893a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 1894a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1895a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang powerUpAsync(FmUtils.computeFrequency(mCurrentStation)); 1896a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1897a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 1898a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // without headset need show plug in earphone tips 1899a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switchAntennaAsync(mValueHeadSetPlug); 1900a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1901a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1902a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1903a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1904a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check the headset is plug in or plug out 1905a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1906a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true for plug in; false for plug out 1907a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1908a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean isHeadSetIn() { 1909a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (0 == mValueHeadSetPlug); 1910a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1911a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1912a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void focusChanged(int focusState) { 1913a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsAudioFocusHeld = false; 1914a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsNativeScanning || mIsNativeSeeking) { 1915a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // make stop scan from activity call to service. 1916a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // notifyActivityStateChanged(FMRadioListener.LISTEN_SCAN_CANCELED); 1917a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopScan(); 1918a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1919a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1920a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // using handler thread to update audio focus state 1921a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updateAudioFocusAync(focusState); 1922a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1923a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1924a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1925a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Request audio focus 1926a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1927a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true, success; false, fail; 1928a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1929a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean requestAudioFocus() { 19301a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang if (FmUtils.getIsSpeakerModeOnFocusLost(mContext)) { 19311a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang setForceUse(true); 19321a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang FmUtils.setIsSpeakerModeOnFocusLost(mContext, false); 19331a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang } 1934a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsAudioFocusHeld) { 1935a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return true; 1936a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1937a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1938a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int audioFocus = mAudioManager.requestAudioFocus(mAudioFocusChangeListener, 1939a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); 1940a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsAudioFocusHeld = (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == audioFocus); 1941a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mIsAudioFocusHeld; 1942a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1943a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1944a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1945a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Abandon audio focus 1946a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1947a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void abandonAudioFocus() { 1948a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.abandonAudioFocus(mAudioFocusChangeListener); 1949a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsAudioFocusHeld = false; 1950a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1951a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1952a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1953a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Use to interact with other voice related app 1954a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1955a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private final OnAudioFocusChangeListener mAudioFocusChangeListener = 1956a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new OnAudioFocusChangeListener() { 1957a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 1958a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Handle audio focus change ensure message FIFO 1959a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 1960a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param focusChange audio focus change state 1961a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 1962a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 1963a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onAudioFocusChange(int focusChange) { 196487970e48c151b8b9533889f317dad79499ced1a0Benson Huang Log.d(TAG, "onAudioFocusChange " + focusChange); 1965a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switch (focusChange) { 1966a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_LOSS: 1967a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (this) { 1968a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.setParameters("AudioFmPreStop=1"); 1969a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 1970a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang focusChanged(AudioManager.AUDIOFOCUS_LOSS); 1971a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1972a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 1973a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1974a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 1975a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (this) { 1976a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mAudioManager.setParameters("AudioFmPreStop=1"); 1977a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(true); 1978a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang focusChanged(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT); 1979a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1980a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 1981a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 1982a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_GAIN: 1983a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (this) { 1984a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updateAudioFocusAync(AudioManager.AUDIOFOCUS_GAIN); 1985a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1986a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 1987a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 198887970e48c151b8b9533889f317dad79499ced1a0Benson Huang case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 198987970e48c151b8b9533889f317dad79499ced1a0Benson Huang synchronized (this) { 199087970e48c151b8b9533889f317dad79499ced1a0Benson Huang updateAudioFocusAync( 199187970e48c151b8b9533889f317dad79499ced1a0Benson Huang AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK); 199287970e48c151b8b9533889f317dad79499ced1a0Benson Huang } 199387970e48c151b8b9533889f317dad79499ced1a0Benson Huang break; 199487970e48c151b8b9533889f317dad79499ced1a0Benson Huang 1995a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang default: 1996a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 1997a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1998a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 1999a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 2000a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2001a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2002a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Audio focus changed, will send message to handler thread. synchronized to 2003a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * ensure one message can go in this method. 2004a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2005a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param focusState AudioManager state 2006a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2007a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private synchronized void updateAudioFocusAync(int focusState) { 2008a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 2009a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 2010a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_AUDIOFOCUS_CHANGED, focusState); 2011a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_AUDIOFOCUS_CHANGED); 2012a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang msg.setData(bundle); 2013a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.sendMessage(msg); 2014a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2015a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2016a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2017a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Audio focus changed, update FM focus state. 2018a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2019a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param focusState AudioManager state 2020a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2021a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void updateAudioFocus(int focusState) { 2022a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switch (focusState) { 2023a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_LOSS: 2024a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPausedByTransientLossOfFocus = false; 2025a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // play back audio will output with music audio 2026a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // May be affect other recorder app, but the flow can not be 2027a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // execute earlier, 2028a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // It should ensure execute after start/stop record. 2029a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 2030a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int fmState = mFmRecorder.getState(); 2031a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // only handle recorder state, not handle playback state 2032a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (fmState == FmRecorder.STATE_RECORDING) { 2033a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 2034a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_STARTRECORDING_FINISHED); 2035a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 2036a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_STOPRECORDING_FINISHED); 2037a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRecording(); 2038a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2039a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2040a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerDown(); 20411a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang forceToHeadsetMode(); 2042a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2043a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2044a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 2045a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus == POWER_UP) { 2046a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPausedByTransientLossOfFocus = true; 2047a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2048a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // play back audio will output with music audio 2049a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // May be affect other recorder app, but the flow can not be 2050a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // execute earlier, 2051a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // It should ensure execute after start/stop record. 2052a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mFmRecorder != null) { 2053a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int fmState = mFmRecorder.getState(); 2054a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (fmState == FmRecorder.STATE_RECORDING) { 2055a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 2056a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_STARTRECORDING_FINISHED); 2057a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages( 2058a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_STOPRECORDING_FINISHED); 2059a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRecording(); 2060a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2061a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2062a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerDown(); 20631a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang forceToHeadsetMode(); 2064a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2065a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2066a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case AudioManager.AUDIOFOCUS_GAIN: 20671a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang if (FmUtils.getIsSpeakerModeOnFocusLost(mContext)) { 20681a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang setForceUse(true); 20691a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang FmUtils.setIsSpeakerModeOnFocusLost(mContext, false); 20701a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang } 2071a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((mPowerStatus != POWER_UP) && mPausedByTransientLossOfFocus) { 2072a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int bundleSize = 1; 2073a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED); 2074a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED); 2075a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle = new Bundle(bundleSize); 2076a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FM_FREQUENCY, FmUtils.computeFrequency(mCurrentStation)); 2077a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerUp(bundle); 2078a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 207987970e48c151b8b9533889f317dad79499ced1a0Benson Huang setMute(false); 208087970e48c151b8b9533889f317dad79499ced1a0Benson Huang break; 208187970e48c151b8b9533889f317dad79499ced1a0Benson Huang 208287970e48c151b8b9533889f317dad79499ced1a0Benson Huang case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 208387970e48c151b8b9533889f317dad79499ced1a0Benson Huang setMute(true); 2084a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2085a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2086a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang default: 2087a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2088a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2089a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2090a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 20911a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang private void forceToHeadsetMode() { 20921a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang if (mIsSpeakerUsed && isHeadSetIn()) { 20931a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang AudioSystem.setForceUse(FOR_PROPRIETARY, AudioSystem.FORCE_NONE); 20941a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang // save user's option to shared preferences. 20951a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang FmUtils.setIsSpeakerModeOnFocusLost(mContext, true); 20961a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang } 20971a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang } 20981a98ea7ef670ada55259b6caa53cbb1058b05a47Benson Huang 2099a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * FM Radio listener record 2101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private static class Record { 2103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int mHashCode; // hash code 2104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener mCallback; // call back 2105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Register FM Radio listener, activity get service state should call this 2109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * method register FM Radio listener 2110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param callback FM Radio listener 2112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void registerFmRadioListener(FmListener callback) { 2114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRecords) { 2115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // register callback in AudioProfileService, if the callback is 2116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // exist, just replace the event. 2117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Record record = null; 2118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int hashCode = callback.hashCode(); 2119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang final int n = mRecords.size(); 2120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int i = 0; i < n; i++) { 2121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang record = mRecords.get(i); 2122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (hashCode == record.mHashCode) { 2123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 2124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang record = new Record(); 2127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang record.mHashCode = hashCode; 2128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang record.mCallback = callback; 2129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRecords.add(record); 2130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Call back from service to activity 2135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param bundle The message to activity 2137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void notifyActivityStateChanged(Bundle bundle) { 2139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mRecords.isEmpty()) { 2140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRecords) { 2141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Iterator<Record> iterator = mRecords.iterator(); 2142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang while (iterator.hasNext()) { 2143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Record record = (Record) iterator.next(); 2144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener listener = record.mCallback; 2146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (listener == null) { 2148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang iterator.remove(); 2149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 2150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang listener.onCallBack(bundle); 2153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Call back from service to the current request activity 2160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Scan need only notify FmFavoriteActivity if current is FmFavoriteActivity 2161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param bundle The message to activity 2163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void notifyCurrentActivityStateChanged(Bundle bundle) { 2165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!mRecords.isEmpty()) { 2166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "notifyCurrentActivityStateChanged = " + mRecords.size()); 2167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRecords) { 2168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mRecords.size() > 0) { 2169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Record record = mRecords.get(mRecords.size() - 1); 2170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener listener = record.mCallback; 2171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (listener == null) { 2172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mRecords.remove(record); 2173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 2174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang listener.onCallBack(bundle); 2176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Unregister FM Radio listener 2183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param callback FM Radio listener 2185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void unregisterFmRadioListener(FmListener callback) { 2187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang remove(callback.hashCode()); 2188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Remove call back according hash code 2192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param hashCode The call back hash code 2194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void remove(int hashCode) { 2196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang synchronized (mRecords) { 2197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Iterator<Record> iterator = mRecords.iterator(); 2198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang while (iterator.hasNext()) { 2199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Record record = (Record) iterator.next(); 2200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (record.mHashCode == hashCode) { 2201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang iterator.remove(); 2202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Check recording sd card is unmount 2209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param intent The unmount sd card intent 2211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return true or false indicate whether current recording sd card is 2213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * unmount or not 2214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isRecordingCardUnmount(Intent intent) { 2216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang String unmountSDCard = intent.getData().toString(); 2217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "unmount sd card file path: " + unmountSDCard); 2218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return unmountSDCard.equalsIgnoreCase("file://" + sRecordingSdcard) ? true : false; 2219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int[] updateStations(int[] stations) { 2222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "updateStations.firstValidstation:" + Arrays.toString(stations)); 2223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int firstValidstation = mCurrentStation; 2224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int stationNum = 0; 2226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != stations) { 2227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int searchedListSize = stations.length; 2228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsDistanceExceed) { 2229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.cleanSearchedStations(mContext); 2230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int j = 0; j < searchedListSize; j++) { 2231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqSearched = stations[j]; 2232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(freqSearched) && 2233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang !FmStation.isFavoriteStation(mContext, freqSearched)) { 2234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.insertStationToDb(mContext, freqSearched, null); 2235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // get stations from db 2239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationNum = updateDBInLocation(stations); 2240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "updateStations.firstValidstation:" + firstValidstation + 2244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ",stationNum:" + stationNum); 2245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return (new int[] { 2246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang firstValidstation, stationNum 2247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }); 2248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * update DB, keep favorite and rds which is searched this time, 2252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * delete rds from db which is not searched this time. 2253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param stations 2254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return number of valid searched stations 2255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private int updateDBInLocation(int[] stations) { 2257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int stationNum = 0; 2258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int searchedListSize = stations.length; 2259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang ArrayList<Integer> stationsInDB = new ArrayList<Integer>(); 2260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Cursor cursor = null; 2261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang try { 2262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // get non favorite stations 2263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang cursor = mContext.getContentResolver().query(Station.CONTENT_URI, 2264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang new String[] { FmStation.Station.FREQUENCY }, 2265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.Station.IS_FAVORITE + "=0", 2266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang null, FmStation.Station.FREQUENCY); 2267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((null != cursor) && cursor.moveToFirst()) { 2268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang do { 2270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqInDB = cursor.getInt(cursor.getColumnIndex( 2271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.Station.FREQUENCY)); 2272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationsInDB.add(freqInDB); 2273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } while (cursor.moveToNext()); 2274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2276a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "updateDBInLocation, insertSearchedStation cursor is null"); 2277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } finally { 2279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (null != cursor) { 2280a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang cursor.close(); 2281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int listSizeInDB = stationsInDB.size(); 2285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // delete station if db frequency is not in searched list 2286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int i = 0; i < listSizeInDB; i++) { 2287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqInDB = stationsInDB.get(i); 2288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int j = 0; j < searchedListSize; j++) { 2289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqSearched = stations[j]; 2290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (freqInDB == freqSearched) { 2291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (j == (searchedListSize - 1) && freqInDB != freqSearched) { 2294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // delete from db 2295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.deleteStationInDb(mContext, freqInDB); 2296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // add to db if station is not in db 2301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang for (int j = 0; j < searchedListSize; j++) { 2302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int freqSearched = stations[j]; 2303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(freqSearched)) { 2304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stationNum++; 2305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!stationsInDB.contains(freqSearched) 2306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang && !FmStation.isFavoriteStation(mContext, freqSearched)) { 2307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // insert to db 2308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmStation.insertStationToDb(mContext, freqSearched, ""); 2309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return stationNum; 2313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * The background handler 2317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang class FmRadioServiceHandler extends Handler { 2319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public FmRadioServiceHandler(Looper looper) { 2320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super(looper); 2321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 2324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void handleMessage(Message msg) { 2325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle; 2326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isPowerup = false; 2327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isSwitch = true; 2328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang switch (msg.what) { 2330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // power up 2332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_POWERUP_FINISHED: 2333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerUp(bundle); 2335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // power down 2338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_POWERDOWN_FINISHED: 2339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang handlePowerDown(); 2340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2342a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // fm exit 2343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_FM_EXIT: 2344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsSpeakerUsed) { 2345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setForceUse(false); 2346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2347a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang powerDown(); 2348a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang closeDevice(); 2349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(1); 2351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_FM_EXIT); 2352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2353a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Finish favorite when exit FM 2354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (sExitListener != null) { 2355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sExitListener.onExit(); 2356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2359a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // switch antenna 23606e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang case FmListener.MSGID_SWITCH_ANTENNA: 2361a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 23626e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang int value = bundle.getInt(FmListener.SWITCH_ANTENNA_VALUE); 2363a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2364a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if ear phone insert, need dismiss plugin earphone 2365a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // dialog 2366a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if earphone plug out and it is not play recorder 2367a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // state, show plug dialog. 2368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (0 == value) { 2369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // powerUpAsync(FMRadioUtils.computeFrequency(mCurrentStation)); 2370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 23716e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang FmListener.MSGID_SWITCH_ANTENNA); 23726e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, true); 2373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2375a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // ear phone plug out, and recorder state is not 2376a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // play recorder state, 2377a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // show dialog. 2378a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mRecordState != FmRecorder.STATE_PLAYBACK) { 2379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 23806e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang FmListener.MSGID_SWITCH_ANTENNA); 23816e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, false); 2382a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2383a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2384a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2385a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2387a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // tune to station 2388a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_TUNE_FINISHED: 2389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2390a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float tuneStation = bundle.getFloat(FM_FREQUENCY); 2391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isTune = tuneStation(tuneStation); 2392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if tune fail, pass current station to update ui 2393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isTune) { 2394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang tuneStation = FmUtils.computeFrequency(mCurrentStation); 2395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2396a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(3); 2397a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 2398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_TUNE_FINISHED); 2399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_TUNE, isTune); 2400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FmListener.KEY_TUNE_TO_STATION, tuneStation); 2401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // seek to station 2405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SEEK_FINISHED: 2406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsSeeking = true; 2408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float seekStation = seekStation(bundle.getFloat(FM_FREQUENCY), 2409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.getBoolean(OPTION)); 2410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isStationTunningSuccessed = false; 2411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int station = FmUtils.computeStation(seekStation); 2412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(station)) { 2413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isStationTunningSuccessed = tuneStation(seekStation); 2414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if tune fail, pass current station to update ui 2416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isStationTunningSuccessed) { 2417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang seekStation = FmUtils.computeFrequency(mCurrentStation); 2418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(2); 2420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 2421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_TUNE_FINISHED); 2422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_TUNE, isStationTunningSuccessed); 2423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putFloat(FmListener.KEY_TUNE_TO_STATION, seekStation); 2424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsSeeking = false; 2426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // start scan 2429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SCAN_FINISHED: 2430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int[] stations = null; 2431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int[] result = null; 2432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int scanTuneStation = 0; 2433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isScan = true; 2434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsScanning = true; 2435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (powerUp(FmUtils.DEFAULT_STATION_FLOAT)) { 2436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stations = startScan(); 2437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // check whether cancel scan 2440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if ((null != stations) && stations[0] == -100) { 2441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isScan = false; 2442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang result = new int[] { 2443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang -1, 0 2444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang }; 2445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang result = updateStations(stations); 2447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang scanTuneStation = result[0]; 2448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang tuneStation(FmUtils.computeFrequency(mCurrentStation)); 2449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /* 2452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * if there is stop command when scan, so it needs to mute 2453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * fm avoid fm sound come out. 2454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mIsAudioFocusHeld) { 2456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(false); 2457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(4); 2459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, 2460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmListener.MSGID_SCAN_FINISHED); 2461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang //bundle.putInt(FmListener.KEY_TUNE_TO_STATION, scanTuneStation); 2462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_STATION_NUM, result[1]); 2463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putBoolean(FmListener.KEY_IS_SCAN, isScan); 24647f521982b1b9c471af1c927a3a45901bb2cbd791Benson Huang 24657f521982b1b9c471af1c927a3a45901bb2cbd791Benson Huang mIsScanning = false; 2466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // Only notify the newest request activity 2467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyCurrentActivityStateChanged(bundle); 2468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // audio focus changed 2471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_AUDIOFOCUS_CHANGED: 2472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int focusState = bundle.getInt(FmListener.KEY_AUDIOFOCUS_CHANGED); 2474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang updateAudioFocus(focusState); 2475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SET_RDS_FINISHED: 2478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRds(bundle.getBoolean(OPTION)); 2480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2481a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SET_MUTE_FINISHED: 2483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setMute(bundle.getBoolean(OPTION)); 2485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_ACTIVE_AF_FINISHED: 2488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang activeAf(); 2489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /********** recording **********/ 2492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_STARTRECORDING_FINISHED: 2493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang startRecording(); 2494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_STOPRECORDING_FINISHED: 2497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang stopRecording(); 2498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_RECORD_MODE_CHANED: 2501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang setRecordingMode(bundle.getBoolean(OPTION)); 2503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang case FmListener.MSGID_SAVERECORDING_FINISHED: 2506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = msg.getData(); 2507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang saveRecording(bundle.getString(RECODING_FILE_NAME)); 2508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang default: 2511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang break; 2512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * handle power down, execute power down and call back to activity. 2519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void handlePowerDown() { 2521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Bundle bundle; 2522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isPowerdown = powerDown(); 2523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(1); 2524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_POWERDOWN_FINISHED); 2525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * handle power up, execute power up and call back to activity. 2530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param bundle power up frequency 2532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private void handlePowerUp(Bundle bundle) { 2534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isPowerUp = false; 2535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isSwitch = true; 2536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float curFrequency = bundle.getFloat(FM_FREQUENCY); 2537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isAntennaAvailable()) { 2539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.d(TAG, "handlePowerUp, earphone is not ready"); 2540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(2); 25416e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_SWITCH_ANTENNA); 25426e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, false); 2543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return; 2545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (powerUp(curFrequency)) { 2547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isFirstTimePlayFm(mContext)) { 2548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isPowerUp = firstPlaying(curFrequency); 2549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang FmUtils.setIsFirstTimePlayFm(mContext); 2550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } else { 2551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isPowerUp = playFrequency(curFrequency); 2552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mPausedByTransientLossOfFocus = false; 2554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle = new Bundle(2); 2556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_POWERUP_FINISHED); 2557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang bundle.putInt(FmListener.KEY_TUNE_TO_STATION, mCurrentStation); 2558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang notifyActivityStateChanged(bundle); 2559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * check FM is foreground or background 2563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public boolean isActivityForeground() { 25653dfd9a996a7d065a95fcee47e698c96163208e2cBenson Huang return (mIsFmMainForeground || mIsFmFavoriteForeground || mIsFmRecordForeground); 2566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * mark FmMainActivity is foreground or not 2570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isForeground 2571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setFmMainActivityForeground(boolean isForeground) { 2573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsFmMainForeground = isForeground; 2574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * mark FmFavoriteActivity activity is foreground or not 2578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param isForeground 2579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setFmFavoriteForeground(boolean isForeground) { 25813dfd9a996a7d065a95fcee47e698c96163208e2cBenson Huang mIsFmFavoriteForeground = isForeground; 2582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 25854b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang * mark FmRecordActivity activity is foreground or not 25864b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang * @param isForeground 25874b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang */ 25884b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang public void setFmRecordActivityForeground(boolean isForeground) { 25893dfd9a996a7d065a95fcee47e698c96163208e2cBenson Huang mIsFmRecordForeground = isForeground; 25904b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang } 25914b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang 25924b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang /** 2593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get the recording sdcard path when staring record 2594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return sdcard path like "/storage/sdcard0" 2596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static String getRecordingSdcard() { 2598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return sRecordingSdcard; 2599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * The listener interface for exit 2603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public interface OnExitListener { 2605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * When Service finish, should notify FmFavoriteActivity to finish 2607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang void onExit(); 2609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Register the listener for exit 2613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param listener The listener want to know the exit event 2615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static void registerExitListener(OnExitListener listener) { 2617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sExitListener = listener; 2618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Unregister the listener for exit 2622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param listener The listener want to know the exit event 2624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public static void unregisterExitListener(OnExitListener listener) { 2626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang sExitListener = null; 2627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Get the latest recording name the show name in save dialog but saved in 2631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * service 2632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @return The latest recording name or null for not modified 2634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public String getModifiedRecordingName() { 2636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return mModifiedRecordingName; 2637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set the latest recording name if modify the default name 2641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * 2642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param name The latest recording name or null for not modified 2643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setModifiedRecordingName(String name) { 2645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mModifiedRecordingName = name; 2646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang @Override 2649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void onTaskRemoved(Intent rootIntent) { 2650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang exitFm(); 2651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang super.onTaskRemoved(rootIntent); 2652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang private boolean firstPlaying(float frequency) { 2655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (mPowerStatus != POWER_UP) { 2656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang Log.w(TAG, "firstPlaying, FM is not powered up"); 2657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return false; 2658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang boolean isSeekTune = false; 2660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang float seekStation = FmNative.seek(frequency, false); 2661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang int station = FmUtils.computeStation(seekStation); 2662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (FmUtils.isValidStation(station)) { 2663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang isSeekTune = FmNative.tune(seekStation); 2664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (isSeekTune) { 2665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang playFrequency(seekStation); 2666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang // if tune fail, pass current station to update ui 2669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang if (!isSeekTune) { 2670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang seekStation = FmUtils.computeFrequency(mCurrentStation); 2671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang return isSeekTune; 2673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set the mIsDistanceExceed 2677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param exceed true is exceed, false is not exceed 2678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setDistanceExceed(boolean exceed) { 2680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mIsDistanceExceed = exceed; 2681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang 2683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang /** 2684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Set notification class name 2685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * @param clsName The target class name of activity 2686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */ 2687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang public void setNotificationClsName(String clsName) { 2688a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang mTargetClassName = clsName; 2689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang } 2690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang} 2691