小米开源便签Notes-源码研究(2)-定时提醒的便签

本篇讲述小米便签中的定时提醒功能。
  便签,可以理解为一件事情,一项任务,有个定时提醒的功能,还是蛮不错的~
  
  小米便签定时功能,是当编辑便签的时候,有个菜单项,选了之后,就弹出一个“日期对话框”,
选择了日期,就设置了定时功能。

下面讲解技术实现的整体思路(很多地方我也不懂,不懂的就搜索)
  
  AndroidManifest.xml配置

<receiver android:name=".ui.AlarmInitReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <receiver
            android:name="net.micode.notes.ui.AlarmReceiver"
            android:process=":remote" >
        </receiver>

        <activity
            android:name=".ui.AlarmAlertActivity"
            android:label="@string/app_name"
            android:launchMode="singleInstance"
            android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
        </activity>

定义了2个receiver和1个activity。
  
  Broadcast Receiver(广播接收器)是Android中的四大组件之一。
  
  AlarmInitReceiver的配置“android.intent.action.BOOT_COMPLETED”,表明
  Android手机开机后,会发送android.intent.action.BOOT_COMPLETED广播,监听这个广播就能监听开机。
  开机之后,就处理已有的便签,逐条比较日期,如果有到期的,就用AlarmManager提醒用户。
  
  场景1.刚刚提到的,开机的时候,需要处理已有的便签。

public class AlarmInitReceiver extends BroadcastReceiver {
  @Override
    public void onReceive(Context context, Intent intent) {
        long currentDate = System.currentTimeMillis();
        Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
                PROJECTION,
                NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
                new String[] { String.valueOf(currentDate) },
                null);

        if (c != null) {
            if (c.moveToFirst()) {
                do {
                    long alertDate = c.getLong(COLUMN_ALERTED_DATE);
                    Intent sender = new Intent(context, AlarmReceiver.class);
                    sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
                    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
                    AlarmManager alermManager = (AlarmManager) context
                            .getSystemService(Context.ALARM_SERVICE);
                    alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
                } while (c.moveToNext());
            }
            c.close();
        }
    }
}

场景2:编辑便签的时候,需要设置日期。

NodeEditActivity的菜单事件

case R.id.menu_delete_remind:
			mWorkingNote.setAlertDate(0, false);
			break;

  WorkingNote
  public void setAlertDate(long date, boolean set) {
		if (date != mAlertDate) {
			mAlertDate = date;
			mNote.setNoteValue(NoteColumns.ALERTED_DATE,
					String.valueOf(mAlertDate));
		}
		if (mNoteSettingStatusListener != null) {
			mNoteSettingStatusListener.onClockAlertChanged(date, set);
		}
	}

  事件监听器,最终还是NodeEditActivity
  public void onClockAlertChanged(long date, boolean set) {
		/**
		 * User could set clock to an unsaved note, so before setting the alert
		 * clock, we should save the note first
		 */
		if (!mWorkingNote.existInDatabase()) {
			saveNote();
		}
		if (mWorkingNote.getNoteId() > 0) {
			Intent intent = new Intent(this, AlarmReceiver.class);
			intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
					mWorkingNote.getNoteId()));
			PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
					intent, 0);
			AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
			showAlertHeader();
			if (!set) {
				alarmManager.cancel(pendingIntent);
			} else {
				alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent);
			}
		} else {
			/**
			 * There is the condition that user has input nothing (the note is
			 * not worthy saving), we have no note id, remind the user that he
			 * should input something
			 */
			Log.e(TAG, "Clock alert setting error");
			showToast(R.string.error_note_empty_for_clock);
		}
	}

如果用户设置了时间,就通过AlarmManager放一个监听事项。
  
  场景3:提醒广播的最终执行者
  AlarmInitReceiver是负责,系统启动的时候,处理notes。
  AlarmReceiver一直在等待“有note需要提醒用户”的广播。

public class AlarmReceiver extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
		intent.setClass(context, AlarmAlertActivity.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		context.startActivity(intent);
	}
}
 时间到期,提醒的活动是AlarmAlertActivity。
 public class AlarmAlertActivity extends Activity implements OnClickListener,
		OnDismissListener {

		protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);

		final Window win = getWindow();
		win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

		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);
		}

		Intent intent = getIntent();

		try {
			mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
			mSnippet = DataUtils.getSnippetById(this.getContentResolver(),
					mNoteId);
			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();
		if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId,
				Notes.TYPE_NOTE)) {
			showActionDialog();
			playAlarmSound();
		} else {
			finish();
		}
	}
}

需要特别学习的Android机制和类库。
  a.AlarmManager
  AlarmManager,顾名思义,就是“提醒”,是Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播一个指定的Intent。
  简单的说就是我们设定一个时间,然后在该时间到来时,AlarmManager为我们广播一个我们设定的Intent。
  通常我们使用PendingIntent,PendingIntent可以理解为Intent的封装包,简单的说就是在Intent上在加个指定的动作。
  在使用Intent的时候,我们还需要在执行startActivity、startService或sendBroadcast才能使Intent有用。
  而PendingIntent的话就是将这个动作包含在内了。
  b.MediaPlayer播放声音。
  c.RingtoneManager铃声。
  d.PowerManager和WindowManager。
  播放铃声的时候,首先判断PowerManager.isScreenOn判断屏幕是否"亮",如果没亮需要做一些处理。
  WindowManager允许app和窗口交互(The interface that apps use to talk to the window manager. )。
  应该就是在锁屏等情况,做一点特殊处理,还没有具体去细看。
  
  PowerManager,电源管理~
  
  写在最后:
  研究过程中,遇到很多不很懂的代码,网上搜索学习下,基本就知道怎么回事了。
  2011年,4年前就有过2个月时间的学习。
  现在工作好几年了,各种技术都研究或者了解过。
  现在,重新看Android应用开发,难度不是很大。
  对Android有了整体的了解,看了几本书,就可以自己研究写代码了。
  不懂的,网上找找资料,然后自己总结,时间长了,估计1~2年,就算是了熟练工了。
  预计,到2016年下半年,我的Android水平就很不错了。
  在武汉这种二线城市,如果让我搞Android开发,薪资在8k~15k左右吧~
  也许~
  
  
  参考资料
  Android开机广播android.intent.action.BOOT_COMPLETED
  http://my.oschina.net/onlytwo/blog/281892
  
  AndroidManifest.xml文件详解(receiver)
  http://blog.csdn.net/think_soft/article/details/7583047
  
  Android中Broadcast Receiver组件详解
  http://blog.csdn.net/zuolongsnail/article/details/6450156
  
  Android中的AlarmManager的使用
  http://blog.csdn.net/wangxingwu_314/article/details/8060312
  
  【Android笔记】MediaPlayer基本使用方式
  http://blog.csdn.net/ddna/article/details/5176233#reply
  
  Media开发之铃声设置(RingtoneManager)
  http://rocking-kaan-126-com.iteye.com/blog/1228215
  
  Android判断屏幕锁屏的方法总结
  http://www.2cto.com/kf/201404/296615.html
  
  android 之 PowerManager 与电源管理
  http://blog.csdn.net/xieqibao/article/details/6562256

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-24 06:45:10

小米开源便签Notes-源码研究(2)-定时提醒的便签的相关文章

Android开源项目 Universal imageloader 源码研究之项目框架

Universal imageloader 的代码并不复杂 重点是缓存,线程池任务 下面都用UML图进行了绘制 基本使用流程就是 初始化配置,设置Options参数,最后Dispaly提交下载 public static void initImageLoader(Context context) { ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(context); config.thr

小米便签Android源码

支持平台:Android    运行环境:Eclipse    开发语言:Java 下载地址:http://www.devstore.cn/code/info/212.html 源码简介 小米便签Android源码,可以再桌面创建widget. 源码运行截图

开源中国安卓客户端源码学习(一) 渐变启动界面

开源中国安卓客户端源码学习(一) 准备学习安卓开发, 看到网上有人推荐开源中国安卓客户端的源码, 说里面包含了大部分技术, 于是准备好好研究研究. 特开通此系列博客来记录学习过程. 由于是在学习, 经验不足, 里面肯定有很多不对的地方, 望大家指正. 到这里下载源码包,开发环境为Linux下Eclipse,导入源码后有可能会出现android.webkit.CacheManager找不到的错误, 原因是这个类在4.0以上版本的SDK被删除了, 只要下载4.0版本的SDK使用即可. 由于googl

Chrome自带恐龙小游戏的源码研究(完)

在上一篇<Chrome自带恐龙小游戏的源码研究(七)>中研究了恐龙与障碍物的碰撞检测,这一篇主要研究组成游戏的其它要素. 游戏分数记录 如图所示,分数及最高分记录显示在游戏界面的右上角,每达到100分就会出现闪烁特效,游戏第一次gameover时显示历史最高分.分数记录器由DistanceMeter构造函数实现,以下是它的全部代码: 1 DistanceMeter.dimensions = { 2 WIDTH: 10, //每个字符的宽度 3 HEIGHT: 13, //每个字符的高 4 DE

Chrome自带恐龙小游戏的源码研究(七)

在上一篇<Chrome自带恐龙小游戏的源码研究(六)>中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测. 碰撞盒子 游戏中采用的是矩形(非旋转矩形)碰撞.这类碰撞优点是计算比较简单,缺点是对不规则物体的检测不够精确.如果不做更为精细的处理,结果会像下图: 如图所示,两个盒子虽然有重叠部分,但实际情况是恐龙和仙人掌之间并未发生碰撞.为了解决这个问题,需要建立多个碰撞盒子: 不过这样还是有问题,观察图片,恐龙和仙人掌都有四个碰撞盒子,如果每次Game Loop里都对这些盒子进行碰撞检测

Redis源码研究—哈希表

Redis源码研究-哈希表 Category: NoSQL数据库 View: 10,980 Author: Dong 作者:Dong | 新浪微博:西成懂 | 可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明 网址:http://dongxicheng.org/nosql/redis-code-hashtable/ 本博客的文章集合:http://dongxicheng.org/recommend/ 本博客微信公共账号:hadoop123(微信号为:hadoop-123),分享

Chrome自带恐龙小游戏的源码研究(五)

在上一篇<Chrome自带恐龙小游戏的源码研究(四)>中实现了障碍物的绘制及移动,从这一篇开始主要研究恐龙的绘制及一系列键盘动作的实现. 会眨眼睛的恐龙 在游戏开始前的待机界面,如果仔细观察会发现恐龙会时不时地眨眼睛.这是通过交替绘制这两个图像实现的: 可以通过一张图片来了解这个过程: 为实现图片的切换,需要一个计时器timer,并且需要知道两张图片切换的时间间隔msPerFrame.当计时器timer的时间大于切换的时间间隔msPerFrame时,将图片切换到下一张,到达最后一张时又从第一张

live555源码研究(四)------UserAuthenticationDatabase类

一.UserAuthenticationDatabase类作用 1,用户/密码管理 2,鉴权管理 二.类UserAuthenticationDatabase继承关系图                         live555源码研究(四)------UserAuthenticationDatabase类,布布扣,bubuko.com

Chrome自带恐龙小游戏的源码研究(六)

在上一篇<Chrome自带恐龙小游戏的源码研究(五)>中实现了眨眼睛的恐龙,这一篇主要研究恐龙的跳跃. 恐龙的跳跃 游戏通过敲击键盘的Spacebar或者Up来实现恐龙的跳跃.先用一张图来表示整个跳跃的过程: 首先规定向下为正方向,即重力加速度(g)为正,起跳的速度(v)为负,恐龙距离画布上方的距离为yPos: 每一帧动画中,速度都会与重力加速度相加得到新的速度,再用新的速度与yPos相加得到新的yPos,改变恐龙的位置为新的yPos,表现出来为yPos不断减小: 当恐龙升至最高点,此时速度为