/* * 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. */ // 包声明,表明该类所属的包名,用于在项目中进行类的组织和管理 package net.micode.notes.widget; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.util.Log; import android.widget.RemoteViews; import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.ResourceParser; import net.micode.notes.ui.NoteEditActivity; import net.micode.notes.ui.NotesListActivity; // NoteWidgetProvider类是一个抽象类,继承自AppWidgetProvider,用于作为桌面小部件(App Widget)相关功能的基础类,提供了一些通用的小部件操作逻辑以及定义了抽象方法供具体子类实现特定功能 public abstract class NoteWidgetProvider extends AppWidgetProvider { // 定义一个字符串数组,用于指定查询数据库时要获取的列信息,这里包含笔记的ID、背景颜色ID以及摘要信息等列 public static final String [] PROJECTION = new String [] { NoteColumns.ID, NoteColumns.BG_COLOR_ID, NoteColumns.SNIPPET }; // 定义常量,用于表示在查询结果中笔记ID所在的列索引位置,方便后续从游标(Cursor)中获取对应的数据 public static final int COLUMN_ID = 0; // 定义常量,用于表示在查询结果中背景颜色ID所在的列索引位置,方便后续从游标(Cursor)中获取对应的数据 public static final int COLUMN_BG_COLOR_ID = 1; // 定义常量,用于表示在查询结果中摘要信息所在的列索引位置,方便后续从游标(Cursor)中获取对应的数据 public static final int COLUMN_SNIPPET = 2; // 定义用于日志记录的标签,方便在日志输出中识别该类相关的日志信息 private static final String TAG = "NoteWidgetProvider"; // 当桌面小部件被删除时调用的方法,用于处理相关的数据清理等操作 @Override public void onDeleted(Context context, int[] appWidgetIds) { // 创建一个ContentValues实例,用于存储要更新到数据库中的数据 ContentValues values = new ContentValues(); // 将笔记相关列(NoteColumns.WIDGET_ID)的值设置为无效的小部件ID(AppWidgetManager.INVALID_APPWIDGET_ID),表示该笔记不再关联被删除的小部件 values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); // 遍历被删除的小部件ID数组 for (int i = 0; i < appWidgetIds.length; i++) { // 通过内容解析器(context.getContentResolver())更新笔记相关的内容提供器(Notes.CONTENT_NOTE_URI)中的数据,使用设置好的ContentValues进行更新,更新条件为笔记的小部件ID列等于当前遍历到的被删除小部件的ID context.getContentResolver().update(Notes.CONTENT_NOTE_URI, values, NoteColumns.WIDGET_ID + "=?", new String[] { String.valueOf(appWidgetIds[i])}); } } // 私有方法,用于获取与指定小部件ID相关的笔记小部件信息,通过查询数据库来获取相应的数据 private Cursor getNoteWidgetInfo(Context context, int widgetId) { // 使用内容解析器发起查询操作,查询的内容提供器为Notes.CONTENT_NOTE_URI,指定要获取的列信息为之前定义的PROJECTION数组中的列,查询条件为笔记的小部件ID等于传入的小部件ID且父ID不等于回收站文件夹的ID(Notes.ID_TRASH_FOLER),查询条件的参数通过字符串数组传入,最后一个参数null表示不指定排序方式等额外条件 return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, PROJECTION, NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) }, null); } // 受保护的方法,用于更新桌面小部件的显示内容等信息,调用了另一个重载的update方法,并传入默认参数false protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { update(context, appWidgetManager, appWidgetIds, false); } // 私有方法,用于实际更新桌面小部件的显示内容等信息,根据传入的小部件ID数组对每个小部件进行相应的更新操作 private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, boolean privacyMode) { // 遍历传入的小部件ID数组 for (int i = 0; i < appWidgetIds.length; i++) { // 如果小部件ID不是无效的小部件ID(AppWidgetManager.INVALID_APPWIDGET_ID),才进行更新操作 if (appWidgetIds[i]!= AppWidgetManager.INVALID_APPWIDGET_ID) { // 获取默认的背景颜色ID,通过ResourceParser工具类的方法获取(具体获取逻辑在该工具类中实现) int bgId = ResourceParser.getDefaultBgId(context); // 初始化摘要信息为空字符串,后续会根据实际查询结果进行赋值 String snippet = ""; // 创建一个意图(Intent),指定要启动的目标Activity为NoteEditActivity.class,即点击小部件后可能会跳转到的编辑笔记的Activity Intent intent = new Intent(context, NoteEditActivity.class); // 设置意图的标志位,使得如果该Activity已经在栈顶,则不会重新创建实例,而是复用已有的实例(例如避免重复打开同一个编辑页面等情况) intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // 将小部件的ID作为额外数据添加到意图中,方便在目标Activity中获取并使用,通过Notes.INTENT_EXTRA_WIDGET_ID键进行传递 intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); // 将小部件的类型作为额外数据添加到意图中,具体的小部件类型由子类实现的getWidgetType方法获取并传入,通过Notes.INTENT_EXTRA_WIDGET_TYPE键进行传递 intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); // 获取与当前小部件ID相关的笔记小部件信息的游标(Cursor),通过调用getNoteWidgetInfo方法查询数据库获取 Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); // 如果游标不为空且游标可以移动到第一条数据(表示查询到了相关数据) if (c!= null && c.moveToFirst()) { // 如果查询结果的数量大于1,说明出现了同一个小部件ID关联多条消息的异常情况,记录错误日志并关闭游标,然后直接返回,不进行后续更新操作 if (c.getCount() > 1) { Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]); c.close(); return; } // 获取摘要信息列的值,赋值给snippet变量,用于后续在小部件上显示 snippet = c.getString(COLUMN_SNIPPET); // 获取背景颜色ID列的值,赋值给bgId变量,用于设置小部件的背景相关显示 bgId = c.getInt(COLUMN_BG_COLOR_ID); // 将笔记的ID作为额外数据添加到意图中,通过Intent.EXTRA_UID键传递,方便在目标Activity中根据ID进行相应操作 intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); // 设置意图的动作(Action)为查看(Intent.ACTION_VIEW),表示点击小部件可能执行查看笔记的操作(具体行为由目标Activity根据该动作进一步处理) intent.setAction(Intent.ACTION_VIEW); } else { // 如果游标为空或者没有查询到相关数据,设置摘要信息为资源字符串中对应的提示文本(表示小部件没有关联内容) snippet = context.getResources().getString(R.string.widget_havenot_content); // 设置意图的动作(Action)为插入或编辑(Intent.ACTION_INSERT_OR_EDIT),表示点击小部件可能执行新建或编辑笔记的操作(具体行为由目标Activity根据该动作进一步处理) intent.setAction(Intent.ACTION_INSERT_OR_EDIT); } // 如果游标不为空,关闭游标,释放相关资源 if (c!= null) { c.close(); } // 创建一个RemoteViews实例,用于构建桌面小部件的远程视图,传入当前上下文的包名和具体的布局ID(由子类实现的getLayoutId方法获取) RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); // 设置小部件中背景图片资源的ID,通过调用抽象方法getBgResourceId传入背景颜色ID来获取对应的资源ID进行设置 rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); // 将背景颜色ID作为额外数据添加到意图中,通过Notes.INTENT_EXTRA_BACKGROUND_ID键传递,方便在目标Activity中根据背景颜色进行相应操作(可能涉及界面显示等方面的适配) intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); // 根据是否处于隐私模式(privacyMode)来生成不同的PendingIntent实例,用于设置小部件点击事件的响应逻辑 PendingIntent pendingIntent = null; if (privacyMode) { // 如果处于隐私模式,设置小部件上文本显示为隐私模式相关的提示文本(通过资源字符串获取) rv.setTextViewText(R.id.widget_text, context.getString(R.string.widget_under_visit_mode)); // 创建一个PendingIntent,用于启动NotesListActivity(可能是进入某种隐私模式下的列表页面等操作),传入当前上下文、小部件ID以及要启动的意图等参数,并设置标志位为PendingIntent.FLAG_UPDATE_CURRENT,表示如果已存在相同的PendingIntent,则更新其额外数据等内容 pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent( context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); } else { // 如果不处于隐私模式,设置小部件上文本显示为之前获取或设置的摘要信息(snippet) rv.setTextViewText(R.id.widget_text, snippet); // 创建一个PendingIntent,用于启动之前设置的意图(intent,可能是跳转到编辑笔记等相关Activity),传入当前上下文、小部件ID以及要启动的意图等参数,并设置标志位为PendingIntent.FLAG_UPDATE_CURRENT,表示如果已存在相同的PendingIntent,则更新其额外数据等内容 pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, PendingIntent.FLAG_UPDATE_CURRENT); } // 设置小部件中文本视图(R.id.widget_text)的点击事件响应PendingIntent,即点击小部件上的文本区域时会触发相应的意图操作 rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); // 通过AppWidgetManager更新指定小部件ID对应的小部件的远程视图,实现小部件显示内容的更新 appWidgetManager.updateAppWidget(appWidgetIds[i], rv); } } } // 抽象方法,用于获取与指定背景颜色ID对应的背景资源ID,由具体的子类根据实际的资源映射关系来实现该方法,以设置小部件的背景图片等显示相关资源 protected abstract int getBgResourceId(int bgId); // 抽象方法,用于获取小部件对应的布局资源ID,由具体的子类根据不同类型小部件的布局需求来实现该方法,以确定小部件的整体布局样式 protected abstract int getLayoutId(); // 抽象方法,用于获取小部件的类型,由具体的子类根据实际小部件的类型定义来实现该方法,以便在更新等操作中传递准确的小部件类型信息 protected abstract int getWidgetType(); }