【日历】铃声设置为SDcard铃声后卸载SDcard,提示音铃声显示为数字事件响应时无铃声输出

2 篇文章 0 订阅
2 篇文章 0 订阅

最近做项目时,测试提出的一个问题,在解的过程中遇到过问题,现在已解决,特此记录一下。


思路:设置为SD卡上的铃声之后,保存这个铃声的路径,监听SD卡的插拔事件,当SD卡mount上并扫描完文件之后,再去数据库中查保存起来的路径,根据路径找到当前的uri,也就是新的content://media/external/audio/media/+数字


第一步:既然设置铃声为SDcard铃声,那么首先就需要写一个广播来监听T卡的插拔,也就是SDcard的卸载与安装了,代码如下:

/**
 * 
 * function: update the calendar ringtone when external sdcard mounted or boot complete
 */
package com.android.calendar;

import java.io.File;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;

public class CalendarRingtoneUpdateReceiver extends BroadcastReceiver{
	
	private static final String TAG = "CalendarRingtoneExternalReceiver";
	
	private Context mContext;
	
	private static final int CAL_CHECK = 0;
	private static final int CAL_QUIT = 1;
	
	@Override
	public void onReceive(Context arg0, Intent arg1) {
		// TODO Auto-generated method stub
		String action = arg1.getAction();
		Log.d(TAG, "[onReceive] action: "+action);
		
		if(TextUtils.isEmpty(action)) {
			Log.w(TAG, "[onReceive] action is empty! just return.");
			return;
		}
		
		if(action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
			mContext = arg0;
			
			HandlerThread thread = new HandlerThread("CalendarRingtoneExternalReceiver");
			thread.start();
			new CheckExternalHandler(thread.getLooper()).sendEmptyMessage(CAL_CHECK);
		}
	}
	
	private final class CheckExternalHandler extends Handler {

		public CheckExternalHandler(Looper looper) {
			// TODO Auto-generated constructor stub
			super(looper);
		}

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			switch (msg.what) {
			case CAL_CHECK:
				SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
				String ringtoneUriForExternal = null;
				Cursor cursor = null;
				String soundUri = Utils.getRingTonePreference(mContext);
				Log.d(TAG, "[onReceive] soundUri : "+soundUri);
				if(TextUtils.isEmpty(soundUri)
						|| !soundUri.startsWith(GeneralPreferences.NOTIFICATION_RINTONE_PATH_PREFIX)) {
					Log.w(TAG, "soundUri is empty, just continue here.");
					this.sendEmptyMessage(CAL_QUIT);
					break;
				}
				ringtoneUriForExternal = preferences.getString(GeneralPreferences.EXTERNAL_RINGTONE_PATH, null);
				Log.d(TAG, "[onReceive] soundUriForExternal : "+ringtoneUriForExternal);
				if(TextUtils.isEmpty(ringtoneUriForExternal)) {
					Log.w(TAG, "soundUriForExternal is empty, we didn't save the external path before.");
					this.sendEmptyMessage(CAL_QUIT);
					break;
				}
				
				//get the real path and check if the file exists
				cursor = mContext.getContentResolver().query(Uri.parse(soundUri), new String[]{ MediaStore.Audio.Media.DATA }, null, null, null);
				try {
					if(cursor != null && cursor.getCount() > 0) {
						if(cursor.moveToFirst()) {
							String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
							Log.d(TAG, "[onReceive] filePath : " + filePath);
							File ringtoneFile = new File(filePath);
							if(ringtoneFile.exists() && ringtoneFile.isFile()) {
								Log.d(TAG, "The ringtone file " + filePath +" exists, just continue here.");
								Log.d(TAG, "[onReceive] filePath : " + filePath);
								this.sendEmptyMessage(CAL_QUIT);
								break;
							}
						}
						if(cursor != null) {
							cursor.close();
						}
					} 
					
					cursor = GeneralPreferences.getDataFromUri(mContext, ringtoneUriForExternal);
					if(cursor != null && cursor.getCount() > 0) {
						if(cursor.moveToFirst()) {
							soundUri = GeneralPreferences.NOTIFICATION_RINTONE_PATH_PREFIX + cursor.getInt(0);
							Log.d(TAG, "[onReceive] soundUri : " + soundUri);
							Utils.setRingTonePreference(mContext, soundUri);
						}
					} 
					
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				} finally {
					if(cursor != null) {
						cursor.close();
					}
				}
				
				this.sendEmptyMessage(CAL_QUIT);
				break;
			case CAL_QUIT:
				Log.d(TAG, "Processing done, quit now.");
				this.getLooper().quit();
				break;
			default:
				break;
			}
			
		}
		
	}

}
GeneralPreferences.java;
public static final String MEDIA_EXTERNAL_PATH = "content://media/external/";
    public static final String NOTIFICATION_RINTONE_PATH_PREFIX = "content://media/external/audio/media/";
    public static final String EXTERNAL_RINGTONE_PATH = "external_ringtone_path";
    public static final String[] MEDIA_COLUMNS = new String[] {
    	MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
    	"\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"",
    	MediaStore.Audio.Media.TITLE_KEY
    };
 
 
public static Cursor getDataFromUri(Context context, String soundUri) {
    	StringBuilder builder = new StringBuilder();
    	builder.append(MediaStore.Audio.Media.TITLE + " != ''");
    	builder.append(" AND " + MediaStore.Audio.Media.DATA + " = " + "\"" + soundUri + "\"");
    	builder.append(" AND " + MediaStore.Audio.Media.IS_MUSIC + "=1");
    	builder.append(" AND mime_type!='audio/x-ms-wma'");
    	return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 
    			GeneralPreferences.MEDIA_COLUMNS, builder.toString(), null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
    }
 第二步:写了广播,那么就需要在AndroidManifest.xml中注册了,如下: 

<receiver android:name="com.android.calendar.CalendarRingtoneUpdateReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
                <data android:scheme="file"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_SCANNER_FINISHED" />
                <data android:scheme="file" />
            </intent-filter>
        </receiver>

注册时,多了一个扫描文件完成的action,这个是media在扫描完文件后发的广播,T卡重新remount之后,media数据库会重新建立。此时广播的工作已经完成啦。

第三步:开始初始化的工作,就需要在初始化的时候,做判断了:

GeneralPreferences.java 的 onCreate():

String ringToneUri = Utils.getRingTonePreference(activity);
String ringtoneDisplayUri = getRingtoneDisplayUri(ringToneUri);
editor.putString(GeneralPreferences.KEY_ALERTS_RINGTONE, ringtoneDisplayUri).apply();
//Here is not saved, otherwise you can not uninstall the sdcard card, then install sdcard, can not be restored sdcard ringtones
//此处不能保存,因为当设置为sdcard铃声后,卸载sdcard变为默认铃声后,再安装sdcard,会不能恢复sdcard上的铃声
//Utils.setRingTonePreference(activity, ringtoneDisplayUri);
String ringtoneDisplayString = getRingtoneTitleFromUri(activity, ringtoneDisplayUri);

当从Utils.getRingTonePreferences()获取铃声之后,需要对获取到的铃声做判断了:

private String getRingtoneDisplayUri(String ringToneUri) {
    	String soundValue = ringToneUri;
    	if(soundValue != null) {
    		if(soundValue.startsWith(GeneralPreferences.MEDIA_EXTERNAL_PATH)) {
    			try {
					Cursor cursor = getActivity().getContentResolver().query(Uri.parse(soundValue), new String[] {MediaStore.Audio.Media.DATA}, null, null, null);
					if(cursor == null || cursor.getCount() < 1) {
						soundValue = getNewRingtoneUriFromBackupFilePath();
					} else {
						if(cursor.moveToFirst()) {
							String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
							File ringtoneFile = new File(filePath);
							
							if(!ringtoneFile.exists() || !ringtoneFile.isFile()) {
								soundValue = getNewRingtoneUriFromBackupFilePath();
							}
							
						} else {
							soundValue = GeneralPreferences.DEFAULT_RINGTONE;
						}
					}
					cursor.close();
				} catch (Exception e) {
					// TODO: handle exception
					soundValue = GeneralPreferences.DEFAULT_RINGTONE;
				} 
    		}
    	}
    	return soundValue;
    }
    
    private String getNewRingtoneUriFromBackupFilePath() {
    	Cursor cursor = null;
    	String soundValue = null;
    	SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
    	String soundUriForExternal = preferences.getString(GeneralPreferences.EXTERNAL_RINGTONE_PATH, null);
    	
    	if(!TextUtils.isEmpty(soundUriForExternal)) {
    		cursor = getDataFromUri(getActivity(), soundUriForExternal);
    		if(cursor == null || cursor.getCount() < 1) {
    			soundValue = GeneralPreferences.DEFAULT_RINGTONE;
    		} else {
				try {
					if(cursor.moveToFirst()) {
						soundValue = GeneralPreferences.NOTIFICATION_RINTONE_PATH_PREFIX + cursor.getInt(0);
						Utils.setRingTonePreference(getActivity(), soundValue);
					}
				} catch (Exception e) {
					// TODO: handle exception
					soundValue = GeneralPreferences.DEFAULT_RINGTONE;
					e.printStackTrace();
				} finally {
					cursor.close();
				}
			}
    		if(cursor != null) {
    			cursor.close();
    		}
    	} else {
			soundValue = GeneralPreferences.DEFAULT_RINGTONE;
		}
    	return soundValue;
    }
此时,初始化的工作已经完成了。

第四步:需要在选择铃声事件时,如果是设置的sdcard铃声,需要保存一下:

GeneralPreferences.java 的 onPreferenceChange()方法中,当 preference == mRingtone 时,添加:

if(((String) newValue).startsWith(GeneralPreferences.MEDIA_EXTERNAL_PATH)) {
                	saveRingtoneInfoForExternal(activity, (String) newValue);
                } else {
		       removeRingtoneInfoForExternal(activity);
		}
private void saveRingtoneInfoForExternal(Context context, String ringtoneUri) {
    	String data = null;
    	
    	try {
    		Cursor cursor = context.getContentResolver().query(Uri.parse(ringtoneUri), 
					new String[] { MediaStore.Audio.Media.DATA }, null, null, null);
			if(cursor != null && cursor.getCount() > 0) {
				if(cursor.moveToFirst()) {
					data = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
				}
			}
			cursor.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
    	SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    	preferences.edit().putString(GeneralPreferences.EXTERNAL_RINGTONE_PATH, data).commit();
    }
    
    private void removeRingtoneInfoForExternal(Context context) {
    	SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    	preferences.edit().remove(GeneralPreferences.EXTERNAL_RINGTONE_PATH).commit();
    }
至此,选择铃声事件的工作也已经完成了。这时,会发现一个问题,那么就是:

设置为sdcard铃声后,卸载sdcard,设置-常规设置中,通知的铃声,已经变为默认铃声,而且点击进去,也勾选了默认铃声,可是,来日程事件时,并没有任何铃声输出。

问题在于:此时SharePreferences中的铃声,并没有修改,但是SharedPreferences中的铃声并不能修改;因为修改后,卸载sdcard后,安装sdcard后,会无法恢复sdcard铃声。

那么该如何修改呢,后来发现原来是在 初始化Notification 时,获取的铃声文件还是sdcard铃声,此时就需要再次判断。

第五步:修改 Notification 的铃声,如下:

AlertService.java 中的 updateAlertNotification() --> generateAlerts() :

// Add options for a quiet update.
            addNotificationOptions(notification, true, expiredDigestTitle,
                    notificationPrefs.getDefaultVibrate(),
                    notificationPrefs.getRingtoneAndSilence(context),
                    false); /* Do not show the LED for the expired events. */
此时可以选择修改addNotificationOptions() 中的:

// Possibly generate a sound. If 'Silent' is chosen, the ringtone
            // string will be empty.
            notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri
                    .parse(reminderRingtone);
也可以选择修改 getRingtoneAndSilence(),我选择的是后者,如下:

private String getRingtoneAndSilence(Context context) {
            if (ringtone == null) {
                if (quietUpdate) {
                    ringtone = EMPTY_RINGTONE;
                } else {
                    ringtone = Utils.getRingTonePreference(context);
                    if(!TextUtils.isEmpty(ringtone) && ringtone.startsWith(GeneralPreferences.MEDIA_EXTERNAL_PATH)) {
                    	Cursor cursor = context.getContentResolver().query(Uri.parse(ringtone), null, null, null, null);
                    	if(cursor == null || cursor.getCount() < 1) {
                    		ringtone = GeneralPreferences.DEFAULT_RINGTONE;
                    	}
                    	if(cursor != null) {
                    		cursor.close();
                    	}
                    }
                }
            }
            String retVal = ringtone;
            ringtone = EMPTY_RINGTONE;
            return retVal;
        }

至此,大功告成啦。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值