/* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // 包声明,表明该类属于net.micode.notes.ui包,通常用于存放与用户界面相关的类 package net.micode.notes.ui; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnDismissListener; import android.content.Intent; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.os.PowerManager; import android.provider.Settings; import android.view.Window; import android.view.WindowManager; import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.tool.DataUtils; import java.io.IOException; // AlarmAlertActivity类继承自Activity类,意味着它是一个安卓中的Activity,用于展示用户界面。 // 同时实现了OnClickListener和OnDismissListener接口,用于处理对话框的点击和关闭事件 public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { // 用于存储与提醒相关联的笔记的ID,可能后续用于查询、操作该笔记的相关信息 private long mNoteId; // 用于存储笔记的摘要信息,将展示在提醒界面等地方给用户查看 private String mSnippet; // 定义摘要信息的最大长度为60个字符,超过这个长度会进行截断处理 private static final int SNIPPET_PREW_MAX_LEN = 60; // MediaPlayer对象,用于播放提醒的声音,比如闹钟铃声等 MediaPlayer mPlayer; // 重写Activity的onCreate方法,该方法在Activity被创建时调用,用于进行初始化等相关操作 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 请求去除Activity的标题栏,使界面更加简洁,仅在支持该特性的安卓版本和设备上生效 requestWindowFeature(Window.FEATURE_NO_TITLE); // 获取当前Activity的窗口对象,后续用于设置窗口的各种属性 final Window win = getWindow(); // 设置窗口属性,使窗口在屏幕锁定时也能够显示出来,方便用户看到提醒信息 win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); // 调用isScreenOn方法判断屏幕是否处于开启状态,如果屏幕未开启 if (!isScreenOn()) { // 给窗口添加一系列标志位,用于确保屏幕在必要时亮起、保持常亮,并且允许在屏幕锁定时进行相关操作, // 同时设置布局相关的装饰属性,以保证界面能正确显示 win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); } // 获取启动该Activity的Intent对象,Intent中可能携带了与提醒相关的数据,比如笔记的相关信息等 Intent intent = getIntent(); try { // 从Intent携带的数据中解析出笔记的ID,具体是从Intent的数据Uri的路径段中获取第二个元素(索引为1)并转换为长整型 mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); // 通过DataUtils工具类的方法,根据笔记ID从内容提供器中获取笔记的摘要信息 mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); // 如果获取到的摘要信息长度大于设定的最大长度(SNIPPET_PREW_MAX_LEN) mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN? mSnippet.substring(0, SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) : mSnippet; // 则进行截断处理,截取前60个字符,并添加一个特定的提示字符串(从资源文件中获取),否则保持原摘要信息不变 } catch (IllegalArgumentException e) { // 如果在解析笔记ID或者获取摘要信息过程中出现参数异常(比如格式不正确等原因),打印异常堆栈信息,并直接返回,不再执行后续操作 e.printStackTrace(); return; } // 创建一个MediaPlayer实例,用于后续播放提醒声音 mPlayer = new MediaPlayer(); // 通过DataUtils工具类判断该笔记在笔记数据库中是否可见(可能涉及是否被删除、是否处于有效状态等条件判断), // 传入笔记ID以及笔记类型(这里是普通笔记类型Notes.TYPE_NOTE)进行判断 if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { // 如果笔记可见,即满足有效条件,调用showActionDialog方法显示操作对话框,供用户进行相应操作 showActionDialog(); // 调用playAlarmSound方法播放提醒声音 playAlarmSound(); } else { // 如果笔记不可见,直接结束当前Activity,意味着不需要展示提醒相关的界面了 finish(); } } // 私有方法,用于判断屏幕是否处于开启状态 private boolean isScreenOn() { // 获取系统的电源管理服务对象,用于查询屏幕的电源状态等信息 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); // 调用电源管理服务对象的isScreenOn方法,返回屏幕是否开启的布尔值结果 return pm.isScreenOn(); } // 私有方法,用于播放提醒声音,比如播放闹钟铃声等 private void playAlarmSound() { // 通过RingtoneManager获取系统默认的闹钟铃声的Uri,用于指定MediaPlayer要播放的音频资源 Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); // 从系统设置中获取静音模式影响的音频流相关设置值,用于判断在静音模式下闹钟声音的播放情况 int silentModeStreams = Settings.System.getInt(getContentResolver(), Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); // 判断静音模式设置中是否影响闹钟音频流,如果影响(对应位的值不为0) if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM))!= 0) { // 设置MediaPlayer的音频流类型为获取到的静音模式设置值,按照系统静音模式的相关配置来播放声音 mPlayer.setAudioStreamType(silentModeStreams); } else { // 如果静音模式不影响闹钟音频流,设置为正常的闹钟音频流类型,确保闹钟声音正常播放 mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); } try { // 设置MediaPlayer的数据源为获取到的铃声Uri,指定要播放的音频文件等资源 mPlayer.setDataSource(this, url); // 准备MediaPlayer,使其进入可播放状态,这个过程可能涉及加载音频资源、初始化播放相关参数等操作 mPlayer.prepare(); // 设置MediaPlayer为循环播放模式,使提醒声音能够持续播放,直到用户手动停止或者其他相关逻辑结束播放 mPlayer.setLooping(true); // 启动MediaPlayer,开始播放提醒声音 mPlayer.start(); } catch (IllegalArgumentException e) { // 如果在设置数据源等过程中出现参数异常(比如参数不合法等原因),打印异常堆栈信息 e.printStackTrace(); } catch (SecurityException e) { // 如果出现安全相关异常(比如没有权限访问音频资源等原因),打印异常堆栈信息 e.printStackTrace(); } catch (IllegalStateException e) { // 如果MediaPlayer的状态出现异常(比如在不恰当的状态下执行了某些操作等原因),打印异常堆栈信息 e.printStackTrace(); } catch (IOException e) { // 如果在读取数据源等IO操作过程中出现异常(比如音频文件损坏、无法读取等原因),打印异常堆栈信息 e.printStackTrace(); } } // 私有方法,用于创建并显示操作对话框,给用户提供相应的操作选项,比如确认、进入编辑等操作 private void showActionDialog() { // 创建一个AlertDialog的构建器对象,用于方便地构建AlertDialog对话框 AlertDialog.Builder dialog = new AlertDialog.Builder(this); // 设置对话框的标题,这里使用了字符串资源中的应用名称(R.string.app_name对应的字符串)作为标题 dialog.setTitle(R.string.app_name); // 设置对话框的消息内容,展示之前获取到的笔记摘要信息,让用户了解是哪个笔记的提醒 dialog.setMessage(mSnippet); // 设置对话框的确定按钮(通常是一个积极的操作按钮)文本以及点击监听器,点击监听器使用当前类实现的OnClickListener接口, // 意味着点击该按钮会触发当前类中对应的onClick方法 dialog.setPositiveButton(R.string.notealert_ok, this); // 如果屏幕处于开启状态,设置对话框的另一个按钮(可能是进入编辑等操作按钮)文本以及点击监听器,同样使用当前类实现的OnClickListener接口 if (isScreenOn()) { dialog.setNegativeButton(R.string.notealert_enter, this); } // 构建并显示对话框,同时设置对话框的关闭监听器,使用当前类实现的OnDismissListener接口, // 意味着对话框关闭时会触发当前类中对应的onDismiss方法 dialog.show().setOnDismissListener(this); } // 实现OnClickListener接口的方法,用于处理对话框按钮的点击事件,根据点击的按钮不同执行相应的逻辑 public void onClick(DialogInterface dialog, int which) { switch (which) { // 如果点击的是对话框中的否定按钮(具体含义根据设置的文本而定,这里可能是进入编辑按钮等情况) case DialogInterface.BUTTON_NEGATIVE: // 创建一个Intent,用于启动NoteEditActivity,可能是进入对应的笔记编辑界面,方便用户对笔记进行编辑等操作 Intent intent = new Intent(this, NoteEditActivity.class); // 设置Intent的动作,这里设置为查看(ACTION_VIEW)动作,表明是查看相关的操作意图 intent.setAction(Intent.ACTION_VIEW); // 通过Intent的额外数据传递机制,将笔记的ID传递给目标Activity(NoteEditActivity),以便目标Activity能获取并处理对应的笔记 intent.putExtra(Intent.EXTRA_UID, mNoteId); // 启动目标Activity,跳转到对应的笔记编辑界面 startActivity(intent); break; default: break; } } // 实现OnDismissListener接口的方法,当对话框关闭时会被调用,用于执行对话框关闭后的相关操作 public void onDismiss(DialogInterface dialog) { // 调用stopAlarmSound方法停止播放提醒声音,并释放相关的资源,如MediaPlayer占用的内存等 stopAlarmSound(); // 结束当前Activity,关闭提醒相关的界面,完成整个提醒流程 finish(); } // 私有方法,用于停止播放提醒声音,并释放MediaPlayer资源,将其置为null避免后续出现问题 private void stopAlarmSound() { if (mPlayer!= null) { // 停止MediaPlayer的播放,如果正在播放声音则立即停止 mPlayer.stop(); // 释放MediaPlayer占用的资源,使其回到初始未使用状态 mPlayer.release(); // 将mPlayer对象置为null,防止后续误操作该对象,同时也符合资源释放后的正确使用习惯 mPlayer = null; } } }