diff --git a/src/widget/NoteWidgetProvider.java b/src/widget/NoteWidgetProvider.java new file mode 100644 index 0000000..db62ef5 --- /dev/null +++ b/src/widget/NoteWidgetProvider.java @@ -0,0 +1,159 @@ + + +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类 +public abstract class NoteWidgetProvider extends AppWidgetProvider { +// 定义一个字符串数组,包含三个元素,分别是NoteColumns.ID、NoteColumns.BG_COLOR_ID和NoteColumns.SNIPPET + public static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.BG_COLOR_ID, + NoteColumns.SNIPPET + }; +// 定义三个整型常量,分别对应PROJECTION数组中的三个元素的索引 + public static final int COLUMN_ID = 0; + public static final int COLUMN_BG_COLOR_ID = 1; + public static final int COLUMN_SNIPPET = 2; +// 定义一个私有常量字符串,值为"NoteWidgetProvider" + private static final String TAG = "NoteWidgetProvider"; + +// 重写AppWidgetProvider类中的onDeleted方法,该方法在组件被删除时调用 + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + // 创建一个ContentValues对象,用于存储要插入或更新的键值对 + ContentValues values = new ContentValues(); + // 在values中放入一个键值对,键为NoteColumns.WIDGET_ID,值为AppWidgetManager.INVALID_APPWIDGET_ID + values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + // 遍历appWidgetIds数组 + for (int i = 0; i < appWidgetIds.length; i++) { + // 使用context的getContentResolver()方法获取内容解析器,并更新Notes表中对应appWidgetIds[i]的记录的WIDGET_ID为AppWidgetManager.INVALID_APPWIDGET_ID + context.getContentResolver().update(Notes.CONTENT_NOTE_URI, + values, + NoteColumns.WIDGET_ID + "=?", + new String[] { String.valueOf(appWidgetIds[i])}); + } + } + + +// 定义一个私有的获取NoteWidgetInfo的方法,输入参数为Context和widgetId,返回值为Cursor对象,用于查询数据库中的记录 + private Cursor getNoteWidgetInfo(Context context, int widgetId) { + // 调用getContentResolver()方法获取内容解析器,并查询Notes表中id等于widgetId且parentId不等于Notes.ID_TRASH_FOLER的记录,返回结果通过PROJECTION参数指定返回哪些列,查询结果通过Cursor对象返回 + 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方法,输入参数为Context、AppWidgetManager和int数组,该方法用于更新小部件的状态,此版本的update方法不包含隐私模式参数,有一个重载版本包含隐私模式参数 + protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // 调用update方法的重载版本,输入参数为Context、AppWidgetManager、int数组以及隐私模式参数false + update(context, appWidgetManager, appWidgetIds, false); + } + + + // 定义一个私有的更新方法,输入参数为Context、AppWidgetManager、int数组以及隐私模式参数,该方法用于更新小部件的状态 +private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, boolean privacyMode) { + // 遍历appWidgetIds数组 + for (int i = 0; i < appWidgetIds.length; i++) { + // 如果appWidgetIds[i]不等于AppWidgetManager.INVALID_APPWIDGET_ID,则执行以下代码块 + if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { + // 调用ResourceParser类的getDefaultBgId方法获取默认背景颜色id,并通过context获取对应的资源id,将结果存储在bgId变量中 + int bgId = ResourceParser.getDefaultBgId(context); + // 将字符串变量snippet初始化为空字符串 + String snippet = ""; + // 创建一个新的Intent对象,目标为NoteEditActivity类 + Intent intent = new Intent(context, NoteEditActivity.class); + // 设置Intent的标志位为FLAG_ACTIVITY_SINGLE_TOP,表示当Activity已经在任务栈顶时,不创建新的Activity实例,而是通过onNewIntent()回调通知Activity + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + // 向Intent中添加额外的数据,Widget的id和类型 + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); + // 调用getNoteWidgetInfo方法获取与appWidgetIds[i]关联的笔记信息,将结果存储在Cursor对象c中 + Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); + // 如果c不为空且已移动到第一条记录,则执行以下代码块 + if (c != null && c.moveToFirst()) { + // 如果c中的记录数量大于1,则表示存在多个相同widget id的记录,打印错误日志并关闭c,然后返回 + if (c.getCount() > 1) { + Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]); + c.close(); + return; + } + // 从c中获取笔记的片段内容,将结果存储在snippet变量中 + snippet = c.getString(COLUMN_SNIPPET); + // 从c中获取背景颜色id,将结果存储在bgId变量中 + bgId = c.getInt(COLUMN_BG_COLOR_ID); + // 向Intent中添加额外的数据,UID和action类型为Intent.ACTION_VIEW + intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); + intent.setAction(Intent.ACTION_VIEW); + } else { + // 如果c为空或没有移动到第一条记录,则将snippet设置为“widget havenot content”字符串,action类型为Intent.ACTION_INSERT_OR_EDIT + snippet = context.getResources().getString(R.string.widget_havenot_content); + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + } + + if (c != null) { + // 关闭c(此时c为null或已读取完所有数据)以释放资源并避免内存泄漏 + c.close(); + } + + + // 创建一个RemoteViews对象,这个对象可以用来控制widget的内容。 +// RemoteViews允许你操作布局中的视图,并可以将其内容发送到其他设备。 +RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); + +// 设置widget背景图像的资源ID。getBgResourceId()是一个抽象方法,需要子类来实现,返回的是背景图像的资源ID。 +rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); + +// 将背景图像的ID放入intent中,以便于后面使用。 +intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); + +// 开始生成用于启动widget主活动的PendingIntent。这部分是注释,注释内容表示该部分代码的功能。 +/** + * Generate the pending intent to start host for the widget + */ +PendingIntent pendingIntent = null; + +// 判断是否为隐私模式。如果是,执行以下代码块;如果不是,跳过并执行下面的else代码块。 +if (privacyMode) { + // 设置widget的文本内容为"widget正在访问中"。这是在隐私模式下显示的文本。 + rv.setTextViewText(R.id.widget_text, context.getString(R.string.widget_under_visit_mode)); + + // 创建一个PendingIntent,用于启动NotesListActivity。这是一个用于在稍后执行的Intent。 + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); +} else { + // 如果不是隐私模式,设置widget的文本内容为"snippet"。这是一个具体的文本片段,可能来自于某个笔记或其他widget数据源。 + rv.setTextViewText(R.id.widget_text, snippet); + + // 创建一个PendingIntent,用于启动在隐私模式下的主Activity。这是一个用于在稍后执行的Intent。 + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, PendingIntent.FLAG_UPDATE_CURRENT); +} + +// 为widget的文本设置一个点击事件。当用户点击这个文本时,会执行上面创建的PendingIntent,从而启动对应的Activity。 +rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); + +// 使用appWidgetManager来更新指定的widget的视图。这会使得之前对RemoteViews对象的修改生效。 +appWidgetManager.updateAppWidget(appWidgetIds[i], rv); + + protected abstract int getBgResourceId(int bgId); + + protected abstract int getLayoutId(); + + protected abstract int getWidgetType(); +}