android 铃声设置流程

android 铃声设置流程讲解 一【最近更新】

总体思路为 :
1.截取铃声
2.保存入sd卡
3.获取铃声信息
4.将信息和文件路径写入数据库获得铃声Uri
5.将获得的Uri设置为铃声。


核心实现:在
public class DefaultRingtonePreference extends RingtonePreference {
private static final String TAG = "DefaultRingtonePreference";

public DefaultRingtonePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
super.onPrepareRingtonePickerIntent(ringtonePickerIntent);

/*
* Since this preference is for choosing the default ringtone, it
* doesn't make sense to show a 'Default' item.
*/
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
}

@Override
protected void onSaveRingtone(Uri ringtoneUri) { //注意这个方法 他是实现铃声设置的核心方法
RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri);
}

@Override
protected Uri onRestoreRingtone() {
return RingtoneManager.getActualDefaultRingtoneUri(getContext(), getRingtoneType());
}

}

DefaultRingtonePreference 单机后出现的是一个AlertDialog 但是在DefaultRingtonePreference没看见任何构造Dialog的信息 显然构建在父亲那的于是出现RingtonePreference的代码:

public class RingtonePreference extends Preference implements PreferenceManager.OnActivityResultListener {

private static final String TAG = "RingtonePreference";

private int mRingtoneType;
private boolean mShowDefault;
private boolean mShowSilent;

private int mRequestCode;

public RingtonePreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.RingtonePreference, defStyle, 0);
mRingtoneType = a.getInt(com.android.internal.R.styleable.RingtonePreference_ringtoneType, RingtoneManager.TYPE_RINGTONE);
mShowDefault = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showDefault, true);
mShowSilent = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showSilent, true);
a.recycle();
}

public RingtonePreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.ringtonePreferenceStyle);
}

public RingtonePreference(Context context) {
this(context, null);
}

/**
* Returns the sound type(s) that are shown in the picker.
* 
* @return The sound type(s) that are shown in the picker.
* @see #setRingtoneType(int)
*/
public int getRingtoneType() {
return mRingtoneType;
}

/**
* Sets the sound type(s) that are shown in the picker.
* 
* @param type
*            The sound type(s) that are shown in the picker.
* @see RingtoneManager#EXTRA_RINGTONE_TYPE
*/
public void setRingtoneType(int type) {
mRingtoneType = type;
}

/**
* Returns whether to a show an item for the default sound/ringtone.
* 
* @return Whether to show an item for the default sound/ringtone.
*/
public boolean getShowDefault() {
return mShowDefault;
}

/**
* Sets whether to show an item for the default sound/ringtone. The default
* to use will be deduced from the sound type(s) being shown.
* 
* @param showDefault
*            Whether to show the default or not.
* @see RingtoneManager#EXTRA_RINGTONE_SHOW_DEFAULT
*/
public void setShowDefault(boolean showDefault) {
mShowDefault = showDefault;
}

/**
* Returns whether to a show an item for 'Silent'.
* 
* @return Whether to show an item for 'Silent'.
*/
public boolean getShowSilent() {
return mShowSilent;
}

/**
* Sets whether to show an item for 'Silent'.
* 
* @param showSilent
*            Whether to show 'Silent'.
* @see RingtoneManager#EXTRA_RINGTONE_SHOW_SILENT
*/
public void setShowSilent(boolean showSilent) {
mShowSilent = showSilent;
}

@Override
protected void onClick() { //注意这对DefaultRingtonePreference单机事件左的处理有跳转
// Launch the ringtone picker
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
onPrepareRingtonePickerIntent(intent);
getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode);
}

/**
* Prepares the intent to launch the ringtone picker. This can be modified
* to adjust the parameters of the ringtone picker.
* 
* @param ringtonePickerIntent
*            The ringtone picker intent that can be modified by putting
*            extras.
*/
protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {

ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, onRestoreRingtone());

ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, mShowDefault);
if (mShowDefault) {
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, RingtoneManager.getDefaultUri(getRingtoneType()));
}

ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, mShowSilent);
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, mRingtoneType);
}

/**
* Called when a ringtone is chosen.
* <p>
* By default, this saves the ringtone URI to the persistent storage as a
* string.
* 
* @param ringtoneUri
*            The chosen ringtone's {@link Uri}. Can be null.
*/
protected void onSaveRingtone(Uri ringtoneUri) {
persistString(ringtoneUri != null ? ringtoneUri.toString() : "");
}

/**
* Called when the chooser is about to be shown and the current ringtone
* should be marked. Can return null to not mark any ringtone.
* <p>
* By default, this restores the previous ringtone URI from the persistent
* storage.
* 
* @return The ringtone to be marked as the current ringtone.
*/
protected Uri onRestoreRingtone() {
final String uriString = getPersistedString(null);
return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
}

@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}

@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValueObj) {
String defaultValue = (String) defaultValueObj;

/*
* This method is normally to make sure the internal state and UI
* matches either the persisted value or the default value. Since we
* don't show the current value in the UI (until the dialog is opened)
* and we don't keep local state, if we are restoring the persisted
* value we don't need to do anything.
*/
if (restorePersistedValue) {
return;
}

// If we are setting to the default value, we should persist it.
if (!TextUtils.isEmpty(defaultValue)) {
onSaveRingtone(Uri.parse(defaultValue));
}
}

@Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
super.onAttachedToHierarchy(preferenceManager);

preferenceManager.registerOnActivityResultListener(this);
mRequestCode = preferenceManager.getNextRequestCode();
}

public boolean onActivityResult(int requestCode, int resultCode, Intent data) {  //注意该方法回调了子类的onSaveRingtone方法

if (requestCode == mRequestCode) {

if (data != null) {
Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);

if (callChangeListener(uri != null ? uri.toString() : "")) {
onSaveRingtone(uri);
}
}

return true;
}

return false;
}

}
对DefaultRingtonePreference的单机跳转到 RingtonePickerActivity.java 下篇
android 铃声设置流程讲解 二继续讲解
 
 

android 铃声设置流程讲解 二【最近跟新】【完】

package com.android.internal.app;

import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;

import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.provider.Settings;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;

/**
* The {@link RingtonePickerActivity} allows the user to choose one from all of
* the available ringtones. The chosen ringtone's URI will be persisted as a
* string.
* 
* @see RingtoneManager#ACTION_RINGTONE_PICKER
*/
public final class RingtonePickerActivity extends AlertActivity implements AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,
AlertController.AlertParams.OnPrepareListViewListener {

private static final String TAG = "RingtonePickerActivity";

private static final int DELAY_MS_SELECTION_PLAYED = 300;

private RingtoneManager mRingtoneManager;

private Cursor mCursor;
private Handler mHandler;

/** The position in the list of the 'Silent' item. */
private int mSilentPos = -1;

/** The position in the list of the 'Default' item. */
private int mDefaultRingtonePos = -1;

/** The position in the list of the last clicked item. */
private int mClickedPos = -1;

/** The position in the list of the ringtone to sample. */
private int mSampleRingtonePos = -1;

/** Whether this list has the 'Silent' item. */
private boolean mHasSilentItem;

/** The Uri to place a checkmark next to. */
private Uri mExistingUri;

/** The number of static items in the list. */
private int mStaticItemCount;

/** Whether this list has the 'Default' item. */
private boolean mHasDefaultItem;

/** The Uri to play when the 'Default' item is clicked. */
private Uri mUriForDefaultItem;

/**
* A Ringtone for the default ringtone. In most cases, the RingtoneManager
* will stop the previous ringtone. However, the RingtoneManager doesn't
* manage the default ringtone for us, so we should stop this one manually.
*/
private Ringtone mDefaultRingtone;

private DialogInterface.OnClickListener mRingtoneClickListener = new DialogInterface.OnClickListener() {

/*
* On item clicked
*/
public void onClick(DialogInterface dialog, int which) {
// Save the position of most recently clicked item
mClickedPos = which;

// Play clip
playRingtone(which, 0);
}

};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mHandler = new Handler();

Intent intent = getIntent();

/*
* Get whether to show the 'Default' item, and the URI to play when the
* default is clicked
*/
mHasDefaultItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
mUriForDefaultItem = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
if (mUriForDefaultItem == null) {
mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
}

// Get whether to show the 'Silent' item
mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);

// Give the Activity so it can do managed queries
mRingtoneManager = new RingtoneManager(this);

// Get whether to include DRM ringtones
boolean includeDrm = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_INCLUDE_DRM, true);
mRingtoneManager.setIncludeDrm(includeDrm);

// Get the types of ringtones to show
int types = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
if (types != -1) {
mRingtoneManager.setType(types);
}

mCursor = mRingtoneManager.getCursor();

// The volume keys will control the stream that we are choosing a
// ringtone for
setVolumeControlStream(mRingtoneManager.inferStreamType());

// Get the URI whose list item should have a checkmark
mExistingUri = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
//mAlertParams 是父亲的属性
final AlertController.AlertParams p = mAlertParams;   //开始构造Dailog了哦
p.mCursor = mCursor;
p.mOnClickListener = mRingtoneClickListener;//这个事件是对列表选择的时候播放该歌曲
p.mLabelColumn = MediaStore.Audio.Media.TITLE;
p.mIsSingleChoice = true;
p.mOnItemSelectedListener = this;
p.mPositiveButtonText = getString(com.android.internal.R.string.ok);
p.mPositiveButtonListener = this; //选择确定后的事件 在该类onClick中实现
p.mNegativeButtonText = getString(com.android.internal.R.string.cancel);
p.mPositiveButtonListener = this; //取消事件 在该类onClick中实现
p.mOnPrepareListViewListener = this;

p.mTitle = intent.getCharSequenceExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
if (p.mTitle == null) {
p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title);
}

setupAlert();
}

public void onPrepareListView(ListView listView) {

if (mHasDefaultItem) {
mDefaultRingtonePos = addDefaultRingtoneItem(listView);

if (RingtoneManager.isDefault(mExistingUri)) {
mClickedPos = mDefaultRingtonePos;
}
}

if (mHasSilentItem) {
mSilentPos = addSilentItem(listView);

// The 'Silent' item should use a null Uri
if (mExistingUri == null) {
mClickedPos = mSilentPos;
}
}

if (mClickedPos == -1) {
mClickedPos = getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri));
}

// Put a checkmark next to an item.
mAlertParams.mCheckedItem = mClickedPos;
}

/**
* Adds a static item to the top of the list. A static item is one that is
* not from the RingtoneManager.
* 
* @param listView
*            The ListView to add to.
* @param textResId
*            The resource ID of the text for the item.
* @return The position of the inserted item.
*/
private int addStaticItem(ListView listView, int textResId) {
TextView textView = (TextView) getLayoutInflater().inflate(com.android.internal.R.layout.select_dialog_singlechoice, listView, false);
textView.setText(textResId);
listView.addHeaderView(textView);
mStaticItemCount++;
return listView.getHeaderViewsCount() - 1;
}

private int addDefaultRingtoneItem(ListView listView) {
return addStaticItem(listView, com.android.internal.R.string.ringtone_default);
}

private int addSilentItem(ListView listView) {
return addStaticItem(listView, com.android.internal.R.string.ringtone_silent);
}

/*
* On click of Ok/Cancel buttons
*/
public void onClick(DialogInterface dialog, int which) {  //该方法实现保存选择的音乐并放回给调用者
boolean positiveResult = which == BUTTON1; //如果悬着的int which是BUTTON1那么返回真
//意思是单机的未知==1那么就是单机了确定按钮

// Stop playing the previous ringtone
mRingtoneManager.stopPreviousRingtone();

if (positiveResult) {
Intent resultIntent = new Intent();
Uri uri = null;

if (mClickedPos == mDefaultRingtonePos) {
// Set it to the default Uri that they originally gave us
uri = mUriForDefaultItem;
} else if (mClickedPos == mSilentPos) {
// A null Uri is for the 'Silent' item
uri = null;
} else {
uri = mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(mClickedPos));
}

resultIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, uri);
setResult(RESULT_OK, resultIntent);
} else {
setResult(RESULT_CANCELED); //放回该调用者的信息
}

getWindow().getDecorView().post(new Runnable() {
public void run() {
mCursor.deactivate();
}
});

finish();
}

/*
* On item selected via keys
*/
public void onItemSelected(AdapterView parent, View view, int position, long id) {
playRingtone(position, DELAY_MS_SELECTION_PLAYED);
}

public void onNothingSelected(AdapterView parent) {
}

private void playRingtone(int position, int delayMs) { //播放歌曲注意mHandler会调用自己应为本类实现了 Runnable
mHandler.removeCallbacks(this);
mSampleRingtonePos = position;
mHandler.postDelayed(this, delayMs);
}

public void run() {

if (mSampleRingtonePos == mSilentPos) {
return;
}

/*
* Stop the default ringtone, if it's playing (other ringtones will be
* stopped by the RingtoneManager when we get another Ringtone from it.
*/
if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
mDefaultRingtone.stop();
mDefaultRingtone = null;
}

Ringtone ringtone;
if (mSampleRingtonePos == mDefaultRingtonePos) {
if (mDefaultRingtone == null) {
mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem);
}
ringtone = mDefaultRingtone;

/*
* Normally the non-static RingtoneManager.getRingtone stops the
* previous ringtone, but we're getting the default ringtone outside
* of the RingtoneManager instance, so let's stop the previous
* ringtone manually.
*/
mRingtoneManager.stopPreviousRingtone();

} else {
ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));
}

if (ringtone != null) {
ringtone.play();
}
}

@Override
protected void onStop() {
super.onStop();
stopAnyPlayingRingtone();
}

@Override
protected void onPause() {
super.onPause();
stopAnyPlayingRingtone();
}

private void stopAnyPlayingRingtone() {

if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
mDefaultRingtone.stop();
}

if (mRingtoneManager != null) {
mRingtoneManager.stopPreviousRingtone();
}
}

private int getRingtoneManagerPosition(int listPos) {
return listPos - mStaticItemCount;
}

private int getListPosition(int ringtoneManagerPos) {

// If the manager position is -1 (for not found), return that
if (ringtoneManagerPos < 0)
return ringtoneManagerPos;

return ringtoneManagerPos + mStaticItemCount;
}

}

 




posted @ 2015-08-19 15:23  MMLoveMeMM  阅读(1655)  评论(0编辑  收藏  举报