PageRenderTime 32ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/mtk/FmRadio/src/com/mediatek/fmradio/FmRecorder.java

https://gitlab.com/vitek999/device_oukitel_orange
Java | 807 lines | 548 code | 49 blank | 210 comment | 104 complexity | 6f873fcc871cf799d6977289959d1034 MD5 | raw file
  1. /* Copyright Statement:
  2. *
  3. * This software/firmware and related documentation ("MediaTek Software") are
  4. * protected under relevant copyright laws. The information contained herein is
  5. * confidential and proprietary to MediaTek Inc. and/or its licensors. Without
  6. * the prior written permission of MediaTek inc. and/or its licensors, any
  7. * reproduction, modification, use or disclosure of MediaTek Software, and
  8. * information contained herein, in whole or in part, shall be strictly
  9. * prohibited.
  10. *
  11. * MediaTek Inc. (C) 2011-2014. All rights reserved.
  12. *
  13. * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
  14. * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
  15. * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
  16. * ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
  17. * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
  19. * NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
  20. * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
  21. * INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
  22. * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
  23. * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
  24. * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
  25. * SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
  26. * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
  27. * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
  28. * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
  29. * RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
  30. * MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
  31. * CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
  32. *
  33. * The following software/firmware and/or related documentation ("MediaTek
  34. * Software") have been modified by MediaTek Inc. All revisions are subject to
  35. * any receiver's applicable license agreements with MediaTek Inc.
  36. */
  37. package com.mediatek.fmradio;
  38. import android.content.ContentValues;
  39. import android.content.Context;
  40. import android.database.Cursor;
  41. import android.media.MediaPlayer;
  42. import android.media.MediaRecorder;
  43. import android.media.MediaScannerConnection;
  44. import android.net.Uri;
  45. import android.os.Environment;
  46. import android.os.SystemClock;
  47. import android.provider.MediaStore;
  48. import android.text.format.DateFormat;
  49. import android.util.Log;
  50. import java.io.File;
  51. import java.io.IOException;
  52. import java.text.SimpleDateFormat;
  53. import java.util.Date;
  54. import java.util.Locale;
  55. /**
  56. * This class provider interface to recording, stop recording, save recording
  57. * file, play recording file
  58. */
  59. public class FmRecorder implements MediaPlayer.OnCompletionListener,
  60. MediaPlayer.OnErrorListener, MediaRecorder.OnErrorListener {
  61. private static final String TAG = "FmRx/Recorder";
  62. // file prefix
  63. private static final String RECORDING_FILE_PREFIX = "FM";
  64. // file extensition
  65. public static final String RECORDING_FILE_EXTENSION = ".3gpp";
  66. // recording file folder
  67. private static final String FM_RECORD_FOLDER = "FM Recording";
  68. private static final String RECORDING_FILE_TYPE = "audio/3gpp";
  69. private static final String RECORDING_FILE_SOURCE = "FM Recordings";
  70. // error type no sdcard
  71. public static final int ERROR_SDCARD_NOT_PRESENT = 0;
  72. // error type sdcard not have enough space
  73. public static final int ERROR_SDCARD_INSUFFICIENT_SPACE = 1;
  74. // error type can't write sdcard
  75. public static final int ERROR_SDCARD_WRITE_FAILED = 2;
  76. // error type recorder internal error occur
  77. public static final int ERROR_RECORDER_INTERNAL = 3;
  78. // error type player internal error occur
  79. public static final int ERROR_PLAYER_INTERNAL = 4;
  80. // error type recorder state is invalid
  81. public static final int ERROR_RECORDER_INVALID_STATE = 5;
  82. // FM Recorder state not recording and not playing
  83. public static final int STATE_IDLE = 5;
  84. // FM Recorder state recording
  85. public static final int STATE_RECORDING = 6;
  86. // FM Recorder state playing
  87. public static final int STATE_PLAYBACK = 7;
  88. // FM Recorder state invalid, need to check
  89. public static final int STATE_INVALID = -1;
  90. // use to record current FM recorder state
  91. public int mInternalState = STATE_IDLE;
  92. // the recording time after start recording
  93. private long mRecordTime = 0;
  94. // record start time
  95. private long mRecordStartTime = 0;
  96. // current record file
  97. private File mRecordFile = null;
  98. // record current record file is saved by user
  99. private boolean mIsRecordingFileSaved = false;
  100. // listener use for notify service the record state or error state
  101. private OnRecorderStateChangedListener mStateListener = null;
  102. // player use for play recording file
  103. private MediaPlayer mPlayer = null;
  104. // recorder use for record file
  105. private MediaRecorder mRecorder = null;
  106. /**
  107. * Start recording the voice of FM, also check the pre-conditions, if not
  108. * meet, will return an error message to the caller. if can start recording
  109. * success, will set FM record state to recording and notify to the caller
  110. */
  111. public void startRecording(Context context) {
  112. Log.d(TAG, ">> startRecording");
  113. mRecordTime = 0;
  114. // Check external storage
  115. if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
  116. Log.e(TAG, "No external storage available");
  117. setError(ERROR_SDCARD_NOT_PRESENT);
  118. return;
  119. }
  120. String recordingSdcard = FmRadioUtils.getDefaultStoragePath();
  121. // check whether have sufficient storage space, if not will notify
  122. // caller error message
  123. if (!FmRadioUtils.hasEnoughSpace(recordingSdcard)) {
  124. setError(ERROR_SDCARD_INSUFFICIENT_SPACE);
  125. Log.e(TAG, "SD card does not have sufficient space!!");
  126. return;
  127. }
  128. // get external storage directory
  129. File sdDir = new File(recordingSdcard);
  130. Log.d(TAG, "external storage dir = " + sdDir.getAbsolutePath());
  131. File recordingDir = new File(sdDir, FM_RECORD_FOLDER);
  132. // exist a file named FM Recording, so can't create FM recording folder
  133. if (recordingDir.exists() && !recordingDir.isDirectory()) {
  134. Log.e(TAG, "A FILE with name \"FM Recording\" already exists!!");
  135. setError(ERROR_SDCARD_WRITE_FAILED);
  136. return;
  137. } else if (!recordingDir.exists()) { // try to create recording folder
  138. boolean mkdirResult = recordingDir.mkdir();
  139. if (!mkdirResult) { // create recording file failed
  140. setError(ERROR_RECORDER_INTERNAL);
  141. return;
  142. }
  143. }
  144. // create recording temporary file
  145. long curTime = System.currentTimeMillis();
  146. Date date = new Date(curTime);
  147. SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH);
  148. String time = simpleDateFormat.format(date);
  149. StringBuilder stringBuilder = new StringBuilder();
  150. stringBuilder.append(RECORDING_FILE_PREFIX).append(
  151. "_").append(time).append(RECORDING_FILE_EXTENSION);
  152. String name = stringBuilder.toString();
  153. mRecordFile = new File(recordingDir, name);
  154. try {
  155. if (mRecordFile.createNewFile()) {
  156. Log.e(TAG, "createNewFile success");
  157. }
  158. } catch (IOException ioex) {
  159. Log.e(TAG, "IOException while createTempFile: " + ioex);
  160. ioex.printStackTrace();
  161. setError(ERROR_SDCARD_WRITE_FAILED);
  162. return;
  163. }
  164. // set record parameter and start recording
  165. try {
  166. Log.d(TAG, "new record file is:" + mRecordFile.getName());
  167. mRecorder = new MediaRecorder();
  168. Log.d(TAG, "startRecording: create new media record instance");
  169. mRecorder.setOnErrorListener(this);
  170. mRecorder.setAudioSource(MediaRecorder.AudioSource.FM_TUNER);
  171. mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
  172. mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
  173. final int samplingRate = 44100;
  174. mRecorder.setAudioSamplingRate(samplingRate);
  175. final int bitRate = 128000;
  176. mRecorder.setAudioEncodingBitRate(bitRate);
  177. final int channels = 2;
  178. mRecorder.setAudioChannels(channels);
  179. mRecorder.setOutputFile(mRecordFile.getAbsolutePath());
  180. mRecorder.prepare();
  181. mRecordStartTime = SystemClock.elapsedRealtime();
  182. mRecorder.start();
  183. mIsRecordingFileSaved = false;
  184. Log.d(TAG, "startRecording: start");
  185. } catch (IllegalStateException e) {
  186. Log.e(TAG, "IllegalStateException while starting recording!", e);
  187. setError(ERROR_RECORDER_INTERNAL);
  188. return;
  189. } catch (IOException e) {
  190. Log.e(TAG, "IOException while starting recording!", e);
  191. setError(ERROR_RECORDER_INTERNAL);
  192. return;
  193. } catch (RuntimeException e) {
  194. Log.e(TAG, "RuntimeException while start recording", e);
  195. setError(ERROR_RECORDER_INTERNAL);
  196. return;
  197. }
  198. setState(STATE_RECORDING);
  199. Log.d(TAG, "<< startRecording");
  200. }
  201. /**
  202. * Stop recording, compute recording time and update FM recorder state
  203. */
  204. public void stopRecording() {
  205. Log.d(TAG, ">> stopRecording");
  206. if (STATE_RECORDING != mInternalState) {
  207. Log.w(TAG, "stopRecording() called in wrong state!!");
  208. return;
  209. }
  210. mRecordTime = SystemClock.elapsedRealtime() - mRecordStartTime;
  211. stopRecorder();
  212. setState(STATE_IDLE);
  213. Log.d(TAG, "<< stopRecording");
  214. }
  215. /**
  216. * Play current recorded file, if failed notify error message to caller, if
  217. * success update FM recorder state
  218. */
  219. public void startPlayback() {
  220. Log.d(TAG, ">> startPlayback");
  221. if (null == mRecordFile) {
  222. Log.e(TAG, "no file to playback!");
  223. return;
  224. }
  225. mPlayer = new MediaPlayer();
  226. try {
  227. mPlayer.setDataSource(mRecordFile.getAbsolutePath());
  228. Log.d(TAG, "MediaPlayer.setDataSource(" + mRecordFile.getAbsolutePath() + ")");
  229. mPlayer.setOnCompletionListener(this);
  230. mPlayer.setOnErrorListener(this);
  231. mPlayer.prepare();
  232. Log.d(TAG, "MediaPlayer.prepare()");
  233. mPlayer.start();
  234. } catch (IOException e) {
  235. Log.e(TAG, "Exception while trying to playback recording file: " + e);
  236. setError(ERROR_PLAYER_INTERNAL);
  237. return;
  238. } catch (IllegalArgumentException e) {
  239. Log.e(TAG, "Exception while trying to playback recording file: " + e);
  240. setError(ERROR_PLAYER_INTERNAL);
  241. return;
  242. } catch (SecurityException e) {
  243. Log.e(TAG, "Exception while trying to playback recording file: " + e);
  244. setError(ERROR_PLAYER_INTERNAL);
  245. return;
  246. } catch (IllegalStateException e) {
  247. Log.e(TAG, "Exception while trying to playback recording file: " + e);
  248. setError(ERROR_PLAYER_INTERNAL);
  249. return;
  250. }
  251. setState(STATE_PLAYBACK);
  252. Log.d(TAG, "<< startPlayback");
  253. }
  254. /**
  255. * Stop playing the current recording file, and update FM recorder state
  256. */
  257. public void stopPlayback() {
  258. Log.d(TAG, ">> stopPlayback");
  259. if (null == mPlayer || STATE_PLAYBACK != mInternalState ||
  260. (null != mPlayer && !mPlayer.isPlaying())) {
  261. Log.w(TAG, "stopPlayback called in wrong state!!");
  262. // need call back to update button status
  263. setState(STATE_IDLE);
  264. return;
  265. }
  266. stopPlayer();
  267. setState(STATE_IDLE);
  268. Log.d(TAG, "<< stopPlayback");
  269. }
  270. /**
  271. * Compute the current record time
  272. *
  273. * @return The current record time
  274. */
  275. public long recordTime() {
  276. if (STATE_RECORDING == mInternalState) {
  277. mRecordTime = SystemClock.elapsedRealtime() - mRecordStartTime;
  278. }
  279. return mRecordTime;
  280. }
  281. /**
  282. * Get FM recorder current state
  283. *
  284. * @return FM recorder current state
  285. */
  286. public int getState() {
  287. return mInternalState;
  288. }
  289. /**
  290. * Get current recording file name
  291. *
  292. * @return The current recording file name
  293. */
  294. public String getRecordingName() {
  295. if (null != mRecordFile) {
  296. String fileName = mRecordFile.getName();
  297. if (fileName.toLowerCase().endsWith(RECORDING_FILE_EXTENSION)
  298. && fileName.length() > RECORDING_FILE_EXTENSION.length()) {
  299. // remove the extension sub string first
  300. fileName = fileName.substring(0, fileName.length() -
  301. RECORDING_FILE_EXTENSION.length());
  302. }
  303. return fileName;
  304. }
  305. return null;
  306. }
  307. /**
  308. * Get current recording file name with full path
  309. *
  310. * @return The current recording file name with path
  311. */
  312. public String getRecordingNameWithPath() {
  313. if (null != mRecordFile) {
  314. String fileName = mRecordFile.getAbsolutePath();
  315. if (fileName.toLowerCase().endsWith(RECORDING_FILE_EXTENSION)
  316. && fileName.length() > RECORDING_FILE_EXTENSION.length()) {
  317. // remove the extension sub string first
  318. fileName = fileName.substring(0, fileName.length() -
  319. RECORDING_FILE_EXTENSION.length());
  320. }
  321. Log.d(TAG, "getRecordingNameWithPath: fileName is " + fileName);
  322. return fileName;
  323. }
  324. return null;
  325. }
  326. /**
  327. * Save recording with the given name, and save recording file info to
  328. * database
  329. *
  330. * @param context The context
  331. * @param newName The name to override default recording name
  332. */
  333. public void saveRecording(Context context, String newName) {
  334. Log.d(TAG, ">> saveRecording(" + newName + ")");
  335. if (null == mRecordFile) {
  336. Log.e(TAG, "<< saveRecording: recording file is null!");
  337. return;
  338. }
  339. File parentFile = mRecordFile.getParentFile();
  340. if (null == parentFile) {
  341. Log.e(TAG, "<< saveRecording: parent recording file is null!");
  342. return;
  343. }
  344. // rename the recording file with given name
  345. if (null != newName && !newName.equals(getRecordingName())) {
  346. File sdFile = new File(parentFile.getPath(),
  347. newName + RECORDING_FILE_EXTENSION);
  348. if (sdFile.exists()) {
  349. Log.w(TAG, "A file with the same new name will be deleted: " +
  350. sdFile.getAbsolutePath());
  351. if (!sdFile.delete()) {
  352. Log.e(TAG, "can't delete the file already exits");
  353. }
  354. }
  355. if (null != parentFile) {
  356. if (!mRecordFile.renameTo(new File(parentFile.getPath(),
  357. newName + RECORDING_FILE_EXTENSION))) {
  358. Log.e(TAG, "can't rename file, use default name to save");
  359. newName = getRecordingName();
  360. }
  361. mRecordFile = new File(parentFile.getPath(),
  362. newName + RECORDING_FILE_EXTENSION);
  363. }
  364. }
  365. mIsRecordingFileSaved = true;
  366. // save recording file info to database
  367. addCurrentRecordingToDb(context);
  368. Log.d(TAG, "<< saveRecording(" + newName + ")");
  369. }
  370. /**
  371. * Discard current recording file, release recorder and player
  372. */
  373. public void discardRecording() {
  374. Log.d(TAG, ">> discardRecording");
  375. // release recorder
  376. if ((STATE_RECORDING == mInternalState) && (null != mRecorder)) {
  377. stopRecorder();
  378. // release player
  379. } else if ((STATE_PLAYBACK == mInternalState) && (null != mPlayer)) {
  380. stopPlayer();
  381. }
  382. if (null != mRecordFile && !mIsRecordingFileSaved) {
  383. if (!mRecordFile.delete()) {
  384. // deletion failed, possibly due to hot plug out SD card
  385. Log.d(TAG, "discardRecording: deletion failed!");
  386. }
  387. mRecordFile = null;
  388. mRecordStartTime = 0;
  389. mRecordTime = 0;
  390. }
  391. setState(STATE_IDLE);
  392. Log.d(TAG, "<< discardRecording");
  393. }
  394. /**
  395. * Set the callback use to notify FM recorder state and error message
  396. *
  397. * @param listener the callback
  398. */
  399. public void registerRecorderStateListener(OnRecorderStateChangedListener listener) {
  400. mStateListener = listener;
  401. }
  402. /**
  403. * Interface to notify FM recorder state and error message
  404. */
  405. public interface OnRecorderStateChangedListener {
  406. /**
  407. * notify FM recorder state
  408. *
  409. * @param state current FM recorder state
  410. */
  411. void onRecorderStateChanged(int state);
  412. /**
  413. * notify FM recorder error message
  414. *
  415. * @param error error type
  416. */
  417. void onRecorderError(int error);
  418. /**
  419. * notify play FM record file complete
  420. */
  421. void onPlayRecordFileComplete();
  422. }
  423. /**
  424. * When complete current recording file, release player and update FM
  425. * recorder state to idle
  426. *
  427. * @param mp The current player
  428. */
  429. @Override
  430. public void onCompletion(MediaPlayer mp) {
  431. Log.d(TAG, ">> MediaPlayer.onCompletion");
  432. synchronized (this) {
  433. if (null != mPlayer) {
  434. mPlayer.stop();
  435. mPlayer.release();
  436. mPlayer = null;
  437. }
  438. }
  439. setState(STATE_IDLE);
  440. if (null != mStateListener) {
  441. mStateListener.onPlayRecordFileComplete();
  442. }
  443. Log.d(TAG, "<< MediaPlayer.onCompletion");
  444. }
  445. /**
  446. * If player occur error, release player, notify error message, update FM
  447. * recorder state to idle
  448. *
  449. * @param mp The current player
  450. * @param what The error message type
  451. * @param extra The error message extra
  452. * @return true or false indicate handle this error or not
  453. */
  454. @Override
  455. public boolean onError(MediaPlayer mp, int what, int extra) {
  456. Log.e(TAG, "MediaPlayer.onError: what=" + what + ", extra=" + extra);
  457. int error = (MediaPlayer.MEDIA_ERROR_SERVER_DIED == what) ? what : ERROR_PLAYER_INTERNAL;
  458. setError(error);
  459. synchronized (this) {
  460. if (null != mPlayer) {
  461. mPlayer.release();
  462. mPlayer = null;
  463. }
  464. }
  465. if (STATE_PLAYBACK == mInternalState) {
  466. setState(STATE_IDLE);
  467. }
  468. return true;
  469. }
  470. /**
  471. * When recorder occur error, release player, notify error message, and
  472. * update FM recorder state to idle
  473. *
  474. * @param mr The current recorder
  475. * @param what The error message type
  476. * @param extra The error message extra
  477. */
  478. @Override
  479. public void onError(MediaRecorder mr, int what, int extra) {
  480. Log.e(TAG, "MediaRecorder.onError: what=" + what + ", extra=" + extra);
  481. stopRecorder();
  482. setError(ERROR_RECORDER_INTERNAL);
  483. if (STATE_RECORDING == mInternalState) {
  484. setState(STATE_IDLE);
  485. }
  486. }
  487. /**
  488. * Reset FM recorder
  489. */
  490. public void resetRecorder() {
  491. if (mRecorder != null) {
  492. mRecorder.release();
  493. mRecorder = null;
  494. }
  495. if (mPlayer != null) {
  496. mPlayer.release();
  497. mPlayer = null;
  498. }
  499. mRecordFile = null;
  500. mRecordStartTime = 0;
  501. mRecordTime = 0;
  502. mInternalState = STATE_IDLE;
  503. }
  504. /**
  505. * Notify error message to the callback
  506. *
  507. * @param error FM recorder error type
  508. */
  509. private void setError(int error) {
  510. Log.e(TAG, "setError: " + error);
  511. if (null != mStateListener) {
  512. mStateListener.onRecorderError(error);
  513. }
  514. }
  515. /**
  516. * Notify FM recorder state message to the callback
  517. *
  518. * @param state FM recorder current state
  519. */
  520. private void setState(int state) {
  521. mInternalState = state;
  522. if (null != mStateListener) {
  523. mStateListener.onRecorderStateChanged(state);
  524. }
  525. }
  526. /**
  527. * Save recording file info to database
  528. *
  529. * @param context application context
  530. */
  531. public void addCurrentRecordingToDb(final Context context) {
  532. Log.v(TAG, ">> addCurrentRecordingToDb");
  533. if (null == mRecordFile || !mRecordFile.exists()) {
  534. Log.e(TAG, "<< addCurrentRecordingToDb: file does not exists");
  535. return;
  536. }
  537. long curTime = System.currentTimeMillis();
  538. long modDate = mRecordFile.lastModified();
  539. Date date = new Date(curTime);
  540. // java.text.DateFormat dateFormatter = DateFormat.getDateFormat(context);
  541. // java.text.DateFormat timeFormatter = DateFormat.getTimeFormat(context);
  542. String title = getRecordingName();
  543. // StringBuilder stringBuilder = new StringBuilder();
  544. // stringBuilder.append(FM_RECORD_FOLDER).append(" ").append(
  545. // dateFormatter.format(date)).append(" ").append(
  546. // timeFormatter.format(date));
  547. // String artist = stringBuilder.toString();
  548. final int size = 8;
  549. ContentValues cv = new ContentValues(size);
  550. cv.put(MediaStore.Audio.Media.IS_MUSIC, 1);
  551. cv.put(MediaStore.Audio.Media.TITLE, title);
  552. cv.put(MediaStore.Audio.Media.DATA, mRecordFile.getAbsolutePath());
  553. final int oneSecond = 1000;
  554. cv.put(MediaStore.Audio.Media.DATE_ADDED, (int) (curTime / oneSecond));
  555. cv.put(MediaStore.Audio.Media.DATE_MODIFIED, (int) (modDate / oneSecond));
  556. cv.put(MediaStore.Audio.Media.MIME_TYPE, RECORDING_FILE_TYPE);
  557. // cv.put(MediaStore.Audio.Media.ARTIST, artist);
  558. cv.put(MediaStore.Audio.Media.ALBUM, RECORDING_FILE_SOURCE);
  559. cv.put(MediaStore.Audio.Media.DURATION, mRecordTime);
  560. int recordingId = addToMediaDB(context, cv);
  561. if (-1 == recordingId) {
  562. // insert failed
  563. return;
  564. }
  565. int playlistId = getPlaylistId(context);
  566. if (-1 == playlistId) {
  567. // play list not exist, create FM Recording play list
  568. playlistId = createPlaylist(context);
  569. }
  570. if (-1 == playlistId) {
  571. // insert playlist failed
  572. return;
  573. }
  574. // insert item to FM recording play list
  575. addToPlaylist(context, playlistId, recordingId);
  576. // Notify applications listening to the scanner events that
  577. // a recorded audio file just created.
  578. // Connect to mediascanner to update duration
  579. MediaScannerConnection.scanFile(context,
  580. new String[] {
  581. mRecordFile.getAbsolutePath()
  582. }, null, null);
  583. }
  584. private int getPlaylistId(final Context context) {
  585. Cursor playlistCursor = context.getContentResolver().query(
  586. MediaStore.Audio.Playlists.getContentUri("external"),
  587. new String[] {
  588. MediaStore.Audio.Playlists._ID
  589. },
  590. MediaStore.Audio.Playlists.DATA + "=?",
  591. new String[] {
  592. FmRadioUtils.getPlaylistPath(context) + RECORDING_FILE_SOURCE
  593. },
  594. null);
  595. int playlistId = -1;
  596. if (null != playlistCursor) {
  597. try {
  598. if (playlistCursor.moveToFirst()) {
  599. playlistId = playlistCursor.getInt(0);
  600. }
  601. } finally {
  602. playlistCursor.close();
  603. }
  604. }
  605. return playlistId;
  606. }
  607. private int createPlaylist(final Context context) {
  608. final int size = 1;
  609. ContentValues cv = new ContentValues(size);
  610. cv.put(MediaStore.Audio.Playlists.NAME, RECORDING_FILE_SOURCE);
  611. Log.d(TAG, "addToPlaylist: insert playlist");
  612. Uri newPlaylistUri = context.getContentResolver().insert(
  613. MediaStore.Audio.Playlists.getContentUri("external"), cv);
  614. if (newPlaylistUri == null) {
  615. Log.d(TAG, "createPlaylist: insert failed");
  616. return -1;
  617. }
  618. return Integer.valueOf(newPlaylistUri.getLastPathSegment());
  619. }
  620. private int addToMediaDB(final Context context, final ContentValues cv) {
  621. Uri insertResult = null;
  622. int recordingId = -1;
  623. Cursor existItems = context.getContentResolver().query(
  624. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  625. new String[] {
  626. "_id"
  627. },
  628. MediaStore.Audio.Media.DATA + "=?",
  629. new String[] {
  630. mRecordFile.getAbsolutePath()
  631. },
  632. null);
  633. if (null != existItems) {
  634. try {
  635. if (existItems.moveToFirst()) {
  636. // there's already a file with the same name in DB, update
  637. // the item.
  638. recordingId = existItems.getInt(0);
  639. Log.d(TAG, "existing items update recording id" + recordingId);
  640. int rowCnt = context.getContentResolver().update(
  641. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  642. cv,
  643. MediaStore.Audio.Media.DATA + "=?",
  644. new String[] {
  645. mRecordFile.getAbsolutePath()
  646. });
  647. } else {
  648. // not exist file has a same name in DB, insert the item.
  649. Log.d(TAG, "addToMediaDB: insert data");
  650. insertResult = context.getContentResolver().insert(
  651. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cv);
  652. if (null != insertResult) {
  653. recordingId = Integer.valueOf(insertResult.getLastPathSegment());
  654. }
  655. }
  656. } finally {
  657. existItems.close();
  658. existItems = null;
  659. }
  660. }
  661. return recordingId;
  662. }
  663. private void addToPlaylist(final Context context, final int playlistId, final int recordingId) {
  664. if (-1 == playlistId) {
  665. return;
  666. }
  667. Uri membersUri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
  668. Cursor itemInMembers = context.getContentResolver().query(membersUri,
  669. new String[] {
  670. MediaStore.Audio.Playlists.Members.AUDIO_ID
  671. },
  672. MediaStore.Audio.Playlists.Members.AUDIO_ID + "=?",
  673. new String[] {
  674. String.valueOf(recordingId)
  675. }, null);
  676. if (null != itemInMembers) {
  677. try {
  678. if (itemInMembers.moveToFirst()) {
  679. // Item already in playlist member,
  680. // no further actions (modify play_order is difficult,
  681. // since a same audio_id can appear multiple times in one
  682. // playlist
  683. // with different play_order)
  684. Log.d(TAG, "addToPlaylist new item already in playlists.members table");
  685. return;
  686. }
  687. } finally {
  688. itemInMembers.close();
  689. itemInMembers = null;
  690. }
  691. }
  692. Log.d(TAG, "addToPlaylist: query members");
  693. Cursor membersCursor = context.getContentResolver().query(
  694. membersUri,
  695. new String[] {
  696. "count(*)"
  697. },
  698. null,
  699. null,
  700. null);
  701. if (null != membersCursor) {
  702. try {
  703. if (membersCursor.moveToFirst()) {
  704. // insert item to playlist
  705. int base = membersCursor.getInt(0);
  706. final int size = 2;
  707. ContentValues cv = new ContentValues(size);
  708. cv.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(base));
  709. cv.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, recordingId);
  710. Log.d(TAG, "addToPlaylist: insert to members");
  711. context.getContentResolver().insert(membersUri, cv);
  712. }
  713. } finally {
  714. membersCursor.close();
  715. membersCursor = null;
  716. }
  717. }
  718. }
  719. private void stopRecorder() {
  720. synchronized (this) {
  721. if (null != mRecorder) {
  722. try {
  723. mRecorder.stop();
  724. Log.d(TAG, "stopRecorder: stop");
  725. } catch (IllegalStateException ex) {
  726. Log.e(TAG, "IllegalStateException ocurr" + ex);
  727. setError(ERROR_RECORDER_INTERNAL);
  728. } catch (RuntimeException exception) {
  729. // modified for stop recording failed.
  730. // native recorder will throw runtime exception such as
  731. // in case of start recording and stop it immediately
  732. Log.e(TAG, "RuntimeException ocurr" + exception);
  733. setError(ERROR_RECORDER_INTERNAL);
  734. } finally {
  735. mRecorder.release();
  736. Log.d(TAG, "stopRecorder: release");
  737. mRecorder = null;
  738. }
  739. }
  740. }
  741. }
  742. private void stopPlayer() {
  743. synchronized (this) {
  744. if (null != mPlayer) {
  745. try {
  746. mPlayer.stop();
  747. } catch (IllegalStateException e) {
  748. Log.e(TAG, "IllegalStateException while discard recording!");
  749. setError(ERROR_PLAYER_INTERNAL);
  750. return;
  751. } finally {
  752. mPlayer.release();
  753. mPlayer = null;
  754. }
  755. }
  756. }
  757. }
  758. }