/* * 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. */ // 220340038 毛彦翔 2024/4/14 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; public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { private long mNoteId; //便签在库中的检索ID private String mSnippet; //便签提醒时显示的文本 private static final int SNIPPET_PREW_MAX_LEN = 60; MediaPlayer mPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);//通过savedInstanceState获取状态信息 requestWindowFeature(Window.FEATURE_NO_TITLE); //通过requestWindowFeature指定活动窗口的特性(无标题) final Window win = getWindow(); /* *获取当前活动的window对象,并赋值给名为win的final变量 *由于final变量 在赋值后不能再改变, *可以确保后续引用该变量时不会被意外修改 提高代码的可维护性 */ win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); //使window对象win可以在锁屏后仍然显示 即可以覆盖在锁屏界面之上 if (!isScreenOn()) { //检查屏幕是否开启 win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON //保持屏幕唤醒 防止自动息屏 | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON //唤醒屏幕并显示窗口 不明白与66行保持屏幕唤醒的区别 | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON //允许屏幕在保持唤醒的情况下手动锁定屏幕 猜测与66行FLAG_KEEP_SCREEN_ON有关 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); //控制边距 确保布局和内容正常显示 } Intent intent = getIntent(); try { mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); //通过检索ID从库中获取便签内容 mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) : mSnippet; } catch (IllegalArgumentException e) { e.printStackTrace(); return; }//捕获异常信息 打印异常堆栈并直接返回 避免因错误信息导致程序崩溃 mPlayer = new MediaPlayer(); //创建一个MediaPlayer对象用于播放音频 if (DataUtils.visibleInNoteDatabase/*便签是否可见*/(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { showActionDialog(); //笔记存在可见则显示操作对话框 playAlarmSound(); //同时播放警示音 } else { finish(); //笔记不存在或不可见则调用finish函数结束当前活动 关闭页面 } } private boolean isScreenOn() { PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); return pm.isScreenOn(); } //一个通过访问系统电源管理服务检查屏幕是否开启 并返回布尔值的私有方法 private void playAlarmSound() { Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); //获取当前默认铃声的uri并存储在Uri类型的变量url中 Uri是什么类型?写完再查 int silentModeStreams = Settings.System.getInt(getContentResolver(), Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); //从系统设置中获取当前静音模式下受铃声模式影响的流的信息,并存储在一个整数变量中 //作用是什么?没想明白 if ((silentModeStreams/*处于静音模式*/ & (1 << AudioManager.STREAM_ALARM)) != 0) { mPlayer.setAudioStreamType(silentModeStreams); /*这下看懂了 *使用位运算判断静音模式下是否受到铃声模式影响 *与运算结果不为0则表示静音模式下受到铃声模式影响 *受铃声模式影响则直接调用setAudioStreamType(silentModeStreams)设置音频流类型 */ } else { mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); } //否则使用AudioManager.STREAM_ALARM作为银趴留类型 总之 确保播放的铃声会受系统静音模式控制 try { mPlayer.setDataSource(this, url); mPlayer.prepare(); mPlayer.setLooping(true); mPlayer.start(); //设置MediaPlayer的数据源 准备播放铃声并循环播放 不一一注释了 } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //用于捕获可能抛出的异常 发生以上异常则打印异常信息并继续执行 //看不懂上面的注释 查找后发现是IDE自动生成的catch块 } private void showActionDialog() { //显示操作对话框的私有方法 AlertDialog.Builder dialog = new AlertDialog.Builder(this); //构建对话框 在当前活动窗口中显示 dialog.setTitle(R.string.app_name); //设置对话框标题 文本来自app_name 这是什么 dialog.setMessage(mSnippet); //对话框的内容 mSnippet在第46行被定义 这是什么来着 dialog.setPositiveButton(R.string.notealert_ok, this); //某个按钮?没懂 positive按钮是什么 if (isScreenOn()) { dialog.setNegativeButton(R.string.notealert_enter, this); //negative按钮又是什么 晕了 这俩玩意的功能写在哪了 } dialog.show().setOnDismissListener(this); //头好痛 要长脑子了 } DialogInterface.OnClickListener public void onClick(DialogInterface dialog, int which) { //对话框被点击时调用 DialogInterface.OnClickListener接口的实现 switch (which) { // 用Switch分值判断哪个按钮被点击了 为什么不用if-else呢 case DialogInterface.BUTTON_NEGATIVE: //negative按钮被按了! Intent intent = new Intent(this, NoteEditActivity.class); //新建intent对象 调用NoteEditActivity类 intent.setAction(Intent.ACTION_VIEW); //把当前活动状态设置为action_view intent.putExtra(Intent.EXTRA_UID, mNoteId); //加了个什么东西 键为EXTRA_UID,值为mNoteID 检索ID?是把这对键值赋给新建的便签吗 startActivity(intent); //启动。 break; default: //按的不是negative按钮则停止活动 为什么startActivity按的是negative按钮不是positive按钮 break; } } public void onDismiss(DialogInterface dialog) { stopAlarmSound(); finish(); } //最简单的一集 对话框消失则调用这两个函数 停止播放警示音并关闭当前活动 private void stopAlarmSound() { if (mPlayer != null) { //就爱你差对象是否为空 是为了防止空指针吗 mPlayer.stop(); mPlayer.release(); //释放MediaPlayer使用的系统资源 避免资源浪费 mPlayer = null; //将mPlayer设置为空 回收内存空间防止泄露 } } }