28、Android多媒体与通信功能开发实用指南

Android多媒体与通信功能开发实用指南

1. 多媒体处理

1.1 EXIF数据处理

EXIF数据可存储照片的多种元数据,包含日期时间、相机设置(如品牌和型号)、图像设置(如光圈和快门速度)、图像描述和位置等。

读取EXIF属性,可调用ExifInterface对象的getAttribute方法,并传入要读取的属性名称。ExifInterface类包含许多静态的TAG_*常量,可用于访问常见的EXIF元数据。修改EXIF属性,可使用setAttribute方法,传入要读取的属性名称和要设置的值。

以下代码展示了如何从SD卡上存储的文件中读取位置坐标和相机型号,并修改相机制造商详细信息:

File file = new File(Environment.getExternalStorageDirectory(),
"test.jpg");
try {
    ExifInterface exif = new ExifInterface(file.getCanonicalPath());
    // Read the camera model and location attributes
    String model = exif.getAttribute(ExifInterface.TAG_MODEL);
    float[] latLng = new float[2];
    exif.getLatLong(latLng);
    // Set the camera make
    exif.setAttribute(ExifInterface.TAG_MAKE, "My Phone");
} catch (IOException e) {
    Log.d("EXIF", e.getMessage());
}

1.2 向媒体库添加新媒体

默认情况下,应用程序创建的媒体文件其他应用无法访问。因此,将其插入媒体库是个好做法,这样其他应用就能访问了。

Android提供了两种将媒体插入媒体库的方法:
- 使用媒体扫描器 :MediaScannerConnection类提供了一种简单的方法,无需为媒体库内容提供者构建完整记录,就能将新媒体添加到媒体库。在使用scanFile方法对文件进行内容扫描之前,必须调用connect方法并等待与媒体扫描器的连接完成。此调用是异步的,需要实现MediaScannerConnectionClient来通知连接何时建立,也可使用该类通知扫描何时完成,完成后可断开媒体扫描器连接。

以下是使用媒体扫描器将新文件添加到媒体库的示例代码:

MediaScannerConnectionClient mediaScannerClient = new
MediaScannerConnectionClient() {
    private MediaScannerConnection msc = null;
    {
        msc = new MediaScannerConnection(getApplicationContext(), this);
        msc.connect();
    }
    public void onMediaScannerConnected() {
        msc.scanFile("/sdcard/test1.jpg", null);
    }
    public void onScanCompleted(String path, Uri uri) {
        msc.disconnect();
    }
};
  • 手动插入媒体 :可创建一个新的ContentValues对象,并将其插入到相应的媒体库内容提供者中,从而将新媒体添加到媒体库。指定的元数据可包括新媒体文件的标题、时间戳和地理编码信息。同时,必须指定要添加的媒体文件的绝对路径。获取应用程序的ContentResolver,并使用它将新行插入媒体库。插入后,应使用广播Intent宣布媒体文件的可用性。

以下是手动插入媒体的示例代码:

ContentValues content = new ContentValues(3);
content.put(Audio.AudioColumns.TITLE, "TheSoundandtheFury");
content.put(Audio.AudioColumns.DATE_ADDED,
System.currentTimeMillis() / 1000);
content.put(Audio.Media.MIME_TYPE, "audio/amr");
content.put(MediaStore.Audio.Media.DATA, "/sdcard/myoutputfile.mp4");
ContentResolver resolver = getContentResolver();
Uri uri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
content);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));

1.3 原始音频处理

AudioTrack和AudioRecord类可让你直接从设备的音频输入硬件录制音频,并将PCM音频缓冲区直接流式传输到音频硬件进行播放。使用AudioTrack流模式,可近乎实时地处理传入音频并播放,从而在设备上处理传入或传出音频并对原始音频进行信号处理。

1.3.1 使用AudioRecord录制声音

使用AudioRecord类可直接从硬件缓冲区录制音频。创建新的AudioRecord对象时,需指定源、频率、通道配置、音频编码和缓冲区大小。

以下是使用AudioRecord录制原始音频到SD卡文件的示例代码:

int frequency = 11025;
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
File file = new File(Environment.getExternalStorageDirectory(), "raw.pcm");
try {
    file.createNewFile();
} catch (IOException e) {}
try {
    OutputStream os = new FileOutputStream(file);
    BufferedOutputStream bos = new BufferedOutputStream(os);
    DataOutputStream dos = new DataOutputStream(bos);
    int bufferSize = AudioRecord.getMinBufferSize(frequency,
    channelConfiguration,
    audioEncoding);
    short[] buffer = new short[bufferSize];
    AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
    frequency,
    channelConfiguration,
    audioEncoding, bufferSize);
    audioRecord.startRecording();
    while (isRecording) {
        int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
        for (int i = 0; i < bufferReadResult; i++)
            dos.writeShort(buffer[i]);
    }
    audioRecord.stop();
    dos.close();
} catch (Throwable t) {}
1.3.2 使用AudioTrack播放声音

使用AudioTrack类可将原始音频直接播放到硬件缓冲区。创建新的AudioTrack对象时,需指定流模式、频率、通道配置、音频编码类型和要播放的音频长度。

以下是播放之前录制的原始音频的示例代码:

int frequency = 11025/2;
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
File file = new File(Environment.getExternalStorageDirectory(), "raw.pcm");
int audioLength = (int)(file.length()/2);
short[] audio = new short[audioLength];
try {
    InputStream is = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(is);
    DataInputStream dis = new DataInputStream(bis);
    int i = 0;
    while (dis.available() > 0) {
        audio[audioLength] = dis.readShort();
        i++;
    }
    dis.close();
    AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
    frequency,
    channelConfiguration,
    audioEncoding,
    audioLength,
    AudioTrack.MODE_STREAM);
    audioTrack.play();
    audioTrack.write(audio, 0, audioLength);
} catch (Throwable t) {}

1.4 语音识别

自Android 1.5(API级别3)起,Android使用RecognizerIntent类支持语音输入和语音识别。该API可让你使用标准语音输入对话框接受语音输入。

语音识别通过调用startActivityForResult方法并传入指定RecognizerIntent.ACTION_RECOGNIZE_SPEECH动作常量的Intent来启动。启动Intent必须包含RecognizerIntent.EXTRA_LANGUAGE_MODEL额外参数,以指定用于解析输入音频的语言模型,可选值为LANGUAGE_MODEL_FREE_FORM或LANGUAGE_MODEL_WEB_SEARCH。还可使用一些可选的额外参数来控制语言、潜在结果数量和显示提示。

以下是启动英语语音识别、返回一个结果并使用自定义提示的示例代码:

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
"or forever hold your peace");
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH);
startActivityForResult(intent, VOICE_RECOGNITION);

用户完成语音输入后,语音识别引擎会分析和处理结果,并通过onActivityResult处理程序以字符串数组列表的形式返回结果。

以下是获取语音识别结果的示例代码:

@Override
protected void onActivityResult(int requestCode,
int resultCode,
Intent data) {
    if (requestCode == VOICE_VOICE_RECOGNITION && resultCode == RESULT_OK) {
        ArrayList<String> results;
        results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
        // TODO Do something with the recognized voice strings
    }
    super.onActivityResult(requestCode, resultCode, data);
}

2. 电话与短信功能

2.1 电话功能

2.1.1 启动拨号器发起电话呼叫

最佳做法是使用Intent启动拨号器应用来发起新的电话呼叫。使用Intent动作启动拨号器活动,并使用tel:方案指定要拨打的号码作为Intent的数据组件。使用Intent.ACTION_DIAL动作启动拨号器,而不是立即拨号,此动作会启动拨号器活动,传入指定号码,但允许拨号器应用管理呼叫初始化。

以下是拨号的基本代码:

Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:1234567"));
startActivity(intent);
2.1.2 替换原生拨号器

替换原生拨号器应用包括两个步骤:
1. 拦截当前由原生拨号器处理的Intent。
2. 发起并可选地管理呼出电话。

原生拨号器应用会响应与用户按下硬件呼叫按钮、使用tel:方案查看数据或使用tel:方案请求拨号对应的Intent动作。要拦截这些请求,需在新的Activity中包含 标签,监听以下动作:
- Intent.ACTION_CALL_BUTTON:设备硬件呼叫按钮被按下时广播此动作。
- Intent.ACTION_DIAL:应用想要启动拨号器进行电话呼叫时使用的Intent动作。
- Intent.ACTION_VIEW:应用想要查看数据时使用的动作。

以下是包含Intent过滤器的Activity清单片段示例:

<activity
android:name=".MyDialerActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.CALL_BUTTON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tel" />
</intent-filter>
</activity>

启动应用后,可让用户输入或修改要拨打的号码并发起呼出电话。最简单的方法是使用现有的电话堆栈,可使用Intent.ACTION_CALL动作发起呼叫,让系统处理拨号、连接和语音处理,但应用必须具有CALL_PHONE权限。也可通过实现自己的拨号和语音处理框架完全替换呼出电话堆栈。

2.1.3 访问电话和网络属性及状态

通过TelephonyManager可访问电话的许多属性,包括设备、网络、SIM和数据状态详细信息。

以下是访问TelephonyManager的代码:

String srvcName = Context.TELEPHONY_SERVICE;
TelephonyManager telephonyManager = (TelephonyManager)getSystemService(srvcName);
读取电话设备详细信息

使用TelephonyManager可获取电话类型(GSM或CDMA)、唯一ID(IMEI或MEID)、软件版本和号码。除电话类型外,读取其他属性需在应用清单中包含READ_PHONE_STATE权限。

以下是读取电话详细信息的代码:

int phoneType = telephonyManager.getPhoneType();
switch (phoneType) {
    case (TelephonyManager.PHONE_TYPE_CDMA): break;
    case (TelephonyManager.PHONE_TYPE_GSM) : break;
    case (TelephonyManager.PHONE_TYPE_NONE): break;
    default: break;
}
// -- These require READ_PHONE_STATE uses-permission --
String deviceId = telephonyManager.getDeviceId();
String softwareVersion = telephonyManager.getDeviceSoftwareVersion();
String phoneNumber = telephonyManager.getLine1Number();
读取数据连接和传输状态

使用getDataState和getDataActivity方法可分别获取当前数据连接状态和传输活动。

以下是读取数据连接和传输状态的代码:

int dataActivity = telephonyManager.getDataActivity();
int dataState = telephonyManager.getDataState();
switch (dataActivity) {
    case TelephonyManager.DATA_ACTIVITY_IN : break;
    case TelephonyManager.DATA_ACTIVITY_OUT : break;
    case TelephonyManager.DATA_ACTIVITY_INOUT : break;
    case TelephonyManager.DATA_ACTIVITY_NONE : break;
}
switch (dataState) {
    case TelephonyManager.DATA_CONNECTED : break;
    case TelephonyManager.DATA_CONNECTING : break;
    case TelephonyManager.DATA_DISCONNECTED : break;
    case TelephonyManager.DATA_SUSPENDED : break;
}
读取网络详细信息

连接到网络时,可使用TelephonyManager读取移动国家和网络代码(MCC+MNC)、国家ISO代码以及连接的网络类型。这些命令仅在连接到移动网络时有效,CDMA网络可能不可靠。

以下是读取网络详细信息的代码:

String networkCountry = telephonyManager.getNetworkCountryIso();
String networkOperatorId = telephonyManager.getNetworkOperator();
String networkName = telephonyManager.getNetworkOperatorName();
int networkType = telephonyManager.getNetworkType();
switch (networkType) {
    case (TelephonyManager.NETWORK_TYPE_1xRTT) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_CDMA) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_EDGE) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_EVDO_0) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_EVDO_A) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_GPRS) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_HSDPA) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_HSPA) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_HSUPA) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_UMTS) : [ . . . do something . . . ] break;
    case (TelephonyManager.NETWORK_TYPE_UNKNOWN) : [ . . . do something . . . ] break;
    default: break;
}
读取SIM详细信息

如果应用在GSM设备上运行,可从TelephonyManager查询SIM详细信息,包括ISO国家代码、运营商名称、运营商MCC和MNC。获取当前SIM的序列号需在应用清单中包含READ_PHONE_STATE权限。在使用这些方法之前,必须确保SIM处于就绪状态。

以下是读取SIM详细信息的代码:

int simState = telephonyManager.getSimState();
switch (simState) {
    case (TelephonyManager.SIM_STATE_ABSENT): break;
    case (TelephonyManager.SIM_STATE_NETWORK_LOCKED): break;
    case (TelephonyManager.SIM_STATE_PIN_REQUIRED): break;
    case (TelephonyManager.SIM_STATE_PUK_REQUIRED): break;
    case (TelephonyManager.SIM_STATE_UNKNOWN): break;
    case (TelephonyManager.SIM_STATE_READY): {
        String simCountry = telephonyManager.getSimCountryIso();
        String simOperatorCode = telephonyManager.getSimOperator();
        String simOperatorName = telephonyManager.getSimOperatorName();
        String simSerial = telephonyManager.getSimSerialNumber();
        break;
    }
    default: break;
}
2.1.4 监控电话状态、电话活动和数据连接变化

Android电话API可让你监控电话状态、获取来电号码并观察数据连接、信号强度和网络连接的变化。要监控和管理电话状态,应用必须在清单中指定READ_PHONE_STATE权限。

通过扩展PhoneStateListener类可监听并响应电话状态变化事件,包括呼叫状态、小区位置变化、语音邮件和呼叫转移状态、电话服务变化以及移动信号强度变化。

以下是PhoneStateListener骨架类代码:

PhoneStateListener phoneStateListener = new PhoneStateListener() {
    public void onCallForwardingIndicatorChanged(boolean cfi) {}
    public void onCallStateChanged(int state, String incomingNumber) {}
    public void onCellLocationChanged(CellLocation location) {}
    public void onDataActivity(int direction) {}
    public void onDataConnectionStateChanged(int state) {}
    public void onMessageWaitingIndicatorChanged(boolean mwi) {}
    public void onServiceStateChanged(ServiceState serviceState) {}
    public void onSignalStrengthChanged(int asu) {}
};

注册PhoneStateListener时,需使用位掩码指定要监听的事件:

telephonyManager.listen(phoneStateListener,
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
PhoneStateListener.LISTEN_CALL_STATE |
PhoneStateListener.LISTEN_CELL_LOCATION |
PhoneStateListener.LISTEN_DATA_ACTIVITY |
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR |
PhoneStateListener.LISTEN_SERVICE_STATE |
PhoneStateListener.LISTEN_SIGNAL_STRENGTH);

取消注册监听器,可调用listen方法并传入PhoneStateListener.LISTEN_NONE作为位字段参数:

telephonyManager.listen(phoneStateListener,
PhoneStateListener.LISTEN_NONE);
监控来电

监控电话状态的一个常见原因是检测并响应来电。可重写PhoneStateListener实现中的onCallStateChanged方法,并进行注册以接收呼叫状态变化的通知。

以下是监控电话呼叫的代码:

PhoneStateListener callStateListener = new PhoneStateListener() {
    public void onCallStateChanged(int state, String incomingNumber) {
        // TODO React to incoming call.
    }
};
telephonyManager.listen(callStateListener,
PhoneStateListener.LISTEN_CALL_STATE);

onCallStateChanged处理程序接收与来电关联的电话号码,state参数表示当前呼叫状态,有以下三种值:
- TelephonyManager.CALL_STATE_IDLE:电话既不响铃也不在通话中。
- TelephonyManager.CALL_STATE_RINGING:电话正在响铃。
- TelephonyManager.CALL_STATE_OFFHOOK:电话正在通话中。

跟踪小区位置变化

重写PhoneStateListener实现中的onCellLocationChanged方法,可在当前小区位置发生变化时获取通知。注册监听小区位置变化前,需在应用清单中添加ACCESS_COARSE_LOCATION权限。

以下是跟踪小区变化的代码:

PhoneStateListener cellLocationListener = new PhoneStateListener() {
    public void onCellLocationChanged(CellLocation location) {
        GsmCellLocation gsmLocation = (GsmCellLocation)location;
        Toast.makeText(getApplicationContext(),
        String.valueOf(gsmLocation.getCid()),
        Toast.LENGTH_LONG).show();
    }
};
telephonyManager.listen(cellLocationListener,
PhoneStateListener.LISTEN_CELL_LOCATION);
跟踪服务变化

onServiceStateChanged处理程序可跟踪设备小区服务的服务详细信息。使用ServiceState参数可获取当前服务状态的详细信息。

以下是监控服务状态变化的代码:

PhoneStateListener serviceStateListener = new PhoneStateListener() {
    public void onServiceStateChanged(ServiceState serviceState) {
        if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
            String toastText = serviceState.getOperatorAlphaLong();
            Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_SHORT);
        }
    }
};
telephonyManager.listen(serviceStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE);

2.2 短信功能

Android提供了完整的短信功能,可在应用中发送和接收短信。可使用Android API创建自己的短信客户端应用,替代软件栈中自带的原生客户端,也可将短信功能集成到自己的应用中,创建使用短信作为传输层的社交应用。

在后续的详细项目中,可使用SMS Manager创建紧急短信响应器,在紧急情况下,用户可快速或自动回复询问其安全状况的人。

2.2.1 使用 Intent 发送短信和彩信

使用 Intent 可以方便地调用系统的短信或彩信发送界面。以下是使用 Intent 发送短信的示例代码:

Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:123456789"));
intent.putExtra("sms_body", "这是一条测试短信");
startActivity(intent);

上述代码中, ACTION_SENDTO 表示发送短信的动作, smsto: 后面跟上要发送的电话号码, sms_body 用于设置短信的内容。

若要发送彩信,可使用如下代码:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file:///sdcard/image.jpg"));
intent.putExtra("sms_body", "这是一条包含图片的彩信");
startActivity(Intent.createChooser(intent, "选择彩信应用"));

这里, ACTION_SEND 表示发送内容的动作, setType 指定要发送的内容类型, EXTRA_STREAM 用于添加要发送的文件, sms_body 依然是设置短信内容。

2.2.2 使用 SMS Manager 发送短信

SMS Manager 提供了更灵活的短信发送方式,可直接在应用内发送短信而无需调用系统界面。以下是使用 SMS Manager 发送短信的示例代码:

SmsManager smsManager = SmsManager.getDefault();
String phoneNumber = "123456789";
String message = "这是使用 SMS Manager 发送的短信";
smsManager.sendTextMessage(phoneNumber, null, message, null, null);

代码中,首先通过 getDefault 方法获取 SMS Manager 的实例,然后调用 sendTextMessage 方法发送短信,该方法的参数依次为:目标电话号码、短信中心号码(一般为 null )、短信内容、发送状态的 PendingIntent(可设为 null )、送达报告的 PendingIntent(可设为 null )。

2.2.3 处理 incoming SMS 消息

要处理接收到的短信,需要创建一个广播接收器来监听短信接收的广播。以下是一个简单的示例:

public class SmsReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                Object[] pdus = (Object[]) bundle.get("pdus");
                for (Object pdu : pdus) {
                    SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu);
                    String sender = smsMessage.getOriginatingAddress();
                    String message = smsMessage.getMessageBody();
                    // 处理接收到的短信
                    Log.d("SMS", "收到来自 " + sender + " 的短信: " + message);
                }
            }
        }
    }
}

在 AndroidManifest.xml 中注册该广播接收器:

<receiver android:name=".SmsReceiver">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

同时,需要在 AndroidManifest.xml 中添加接收短信的权限:

<uses-permission android:name="android.permission.RECEIVE_SMS" />

2.2.4 实现紧急短信响应器项目

下面详细介绍如何使用 SMS Manager 创建紧急短信响应器。该响应器在紧急情况下,用户可快速或自动回复询问其安全状况的人。

项目思路
  • 监听来电和短信,当收到特定联系人的询问短信时,根据用户设置的紧急状态,自动回复预设的短信。
  • 提供一个界面让用户设置紧急状态和回复内容。
代码实现
  1. 创建紧急状态设置界面 :在布局文件中创建一个简单的界面,包含一个开关用于设置紧急状态,一个输入框用于设置回复内容,一个保存按钮。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <Switch
        android:id="@+id/emergencySwitch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="紧急状态" />

    <EditText
        android:id="@+id/replyEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="设置回复内容" />

    <Button
        android:id="@+id/saveButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="保存设置" />
</LinearLayout>
  1. 在 Activity 中处理设置逻辑
public class EmergencySettingsActivity extends AppCompatActivity {
    private Switch emergencySwitch;
    private EditText replyEditText;
    private Button saveButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_emergency_settings);

        emergencySwitch = findViewById(R.id.emergencySwitch);
        replyEditText = findViewById(R.id.replyEditText);
        saveButton = findViewById(R.id.saveButton);

        saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean isEmergency = emergencySwitch.isChecked();
                String replyMessage = replyEditText.getText().toString();
                // 保存设置到 SharedPreferences
                SharedPreferences preferences = getSharedPreferences("EmergencySettings", MODE_PRIVATE);
                SharedPreferences.Editor editor = preferences.edit();
                editor.putBoolean("isEmergency", isEmergency);
                editor.putString("replyMessage", replyMessage);
                editor.apply();
                Toast.makeText(EmergencySettingsActivity.this, "设置保存成功", Toast.LENGTH_SHORT).show();
            }
        });
    }
}
  1. 在广播接收器中处理短信回复逻辑
public class EmergencySmsReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
            SharedPreferences preferences = context.getSharedPreferences("EmergencySettings", MODE_PRIVATE);
            boolean isEmergency = preferences.getBoolean("isEmergency", false);
            String replyMessage = preferences.getString("replyMessage", "");

            if (isEmergency) {
                Bundle bundle = intent.getExtras();
                if (bundle != null) {
                    Object[] pdus = (Object[]) bundle.get("pdus");
                    for (Object pdu : pdus) {
                        SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu);
                        String sender = smsMessage.getOriginatingAddress();
                        String message = smsMessage.getMessageBody();
                        // 假设特定关键词为 "安全状况"
                        if (message.contains("安全状况")) {
                            SmsManager smsManager = SmsManager.getDefault();
                            smsManager.sendTextMessage(sender, null, replyMessage, null, null);
                        }
                    }
                }
            }
        }
    }
}
  1. 在 AndroidManifest.xml 中注册广播接收器
<receiver android:name=".EmergencySmsReceiver">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

总结

通过上述内容,我们详细介绍了 Android 中的多媒体处理和电话短信功能。在多媒体处理方面,涵盖了 EXIF 数据处理、向媒体库添加新媒体、原始音频处理以及语音识别等内容,每个部分都给出了具体的操作步骤和示例代码。在电话短信功能方面,包括了电话的拨号、状态监控、短信和彩信的发送以及短信的接收处理等,同样提供了详细的代码示例和操作说明。掌握这些知识和技能,能够帮助开发者在 Android 应用开发中更好地实现多媒体和通信相关的功能。

流程图

graph LR
    A[开始] --> B[多媒体处理]
    B --> B1[EXIF数据处理]
    B --> B2[添加新媒体到媒体库]
    B --> B3[原始音频处理]
    B --> B4[语音识别]
    A --> C[电话短信功能]
    C --> C1[电话功能]
    C1 --> C11[启动拨号器]
    C1 --> C12[替换原生拨号器]
    C1 --> C13[访问电话和网络属性]
    C1 --> C14[监控电话状态]
    C --> C2[短信功能]
    C2 --> C21[使用Intent发送短信彩信]
    C2 --> C22[使用SMS Manager发送短信]
    C2 --> C23[处理接收到的短信]
    C2 --> C24[实现紧急短信响应器]
    B1 --> D[结束]
    B2 --> D
    B3 --> D
    B4 --> D
    C11 --> D
    C12 --> D
    C13 --> D
    C14 --> D
    C21 --> D
    C22 --> D
    C23 --> D
    C24 --> D

表格

功能模块 具体功能 关键类或方法
多媒体处理 EXIF 数据处理 ExifInterface 的 getAttribute 和 setAttribute 方法
多媒体处理 添加新媒体到媒体库 MediaScannerConnection、ContentResolver 的 insert 方法
多媒体处理 原始音频处理 AudioRecord、AudioTrack
多媒体处理 语音识别 RecognizerIntent
电话短信功能 电话拨号 Intent.ACTION_DIAL、Intent.ACTION_CALL
电话短信功能 监控电话状态 PhoneStateListener
电话短信功能 发送短信彩信 Intent、SmsManager
电话短信功能 处理接收到的短信 广播接收器监听 android.provider.Telephony.SMS_RECEIVED 广播
基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样统计,通过模拟系统元件的故障修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高校研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更多不确定性因素进行深化研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值