zoukankan      html  css  js  c++  java
  • 小米开源便签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
  • 相关阅读:
    去除phpcms会员登录后头部登陆条的会员名称的括号
    图片左右间隔滚动Jquery特效
    JS判断字符串长度的5个方法
    HTML中&nbsp; &ensp; &emsp; &thinsp;等6种空白空格的区别
    Chrome firefox ie等浏览器空格&nbsp;宽度不一样怎么办
    jQuery延迟加载插件(Lazy Load)详解
    jquery复选框 选中事件 及其判断是否被选中
    手机网页Html代码实现(解决显示页面很小的问题)
    iOS下的按钮css去除原生样式
    用纯css改变下拉列表select框的默认样式(不兼容IE10以下)
  • 原文地址:https://www.cnblogs.com/qitian1/p/6462603.html
Copyright © 2011-2022 走看看