You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MiNote/widget/NoteWidgetProvider.java

208 lines
8.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* 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 是一个抽象基类,用于实现便签应用的桌面小部件功能。
* 它提供了小部件生命周期管理、数据查询和界面更新的核心逻辑,
* 具体的布局和样式由子类实现。
*
* 小部件功能包括:
* - 显示便签内容和背景
* - 支持点击便签打开编辑界面
* - 处理小部件删除事件
* - 支持隐私模式显示
*/
public abstract class NoteWidgetProvider extends AppWidgetProvider {
// 查询便签数据时使用的投影,指定需要获取的列
public static final String [] PROJECTION = new String [] {
NoteColumns.ID, // 便签ID
NoteColumns.BG_COLOR_ID,// 便签背景颜色ID
NoteColumns.SNIPPET // 便签内容摘要
};
// 投影列的索引常量,用于快速访问查询结果
public static final int COLUMN_ID = 0;
public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_SNIPPET = 2;
private static final String TAG = "NoteWidgetProvider";
/**
* 当一个或多个小部件被删除时调用。
* 此方法会将被删除小部件关联的便签的 widget_id 字段重置为 INVALID_APPWIDGET_ID
* 以解除便签与已删除小部件的关联。
*
* @param context 应用上下文
* @param appWidgetIds 被删除的小部件ID数组
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
ContentValues values = new ContentValues();
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
// 遍历所有被删除的小部件ID更新对应的便签记录
for (int i = 0; i < appWidgetIds.length; i++) {
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
new String[] { String.valueOf(appWidgetIds[i])});
}
}
/**
* 查询与指定小部件ID关联的便签信息。
* 只查询不在回收站中的便签。
*
* @param context 应用上下文
* @param widgetId 小部件ID
* @return 返回包含便签信息的Cursor使用完后需要关闭
*/
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
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);
}
/**
* 更新一组小部件的显示内容,默认不使用隐私模式。
*
* @param context 应用上下文
* @param appWidgetManager AppWidgetManager实例
* @param appWidgetIds 需要更新的小部件ID数组
*/
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
update(context, appWidgetManager, appWidgetIds, false);
}
/**
* 更新一组小部件的显示内容,可以选择是否使用隐私模式。
* 隐私模式下会显示通用提示信息,而不是具体的便签内容。
*
* @param context 应用上下文
* @param appWidgetManager AppWidgetManager实例
* @param appWidgetIds 需要更新的小部件ID数组
* @param privacyMode 是否使用隐私模式
*/
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
// 遍历所有需要更新的小部件ID
for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
// 设置默认值
int bgId = ResourceParser.getDefaultBgId(context);
String snippet = "";
// 创建点击便签时要启动的Intent
Intent intent = new Intent(context, NoteEditActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
// 查询与小部件关联的便签信息
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c != null && c.moveToFirst()) {
// 检查是否存在多个便签关联到同一个小部件ID异常情况
if (c.getCount() > 1) {
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
c.close();
return;
}
// 从Cursor中获取便签内容和背景信息
snippet = c.getString(COLUMN_SNIPPET);
bgId = c.getInt(COLUMN_BG_COLOR_ID);
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
intent.setAction(Intent.ACTION_VIEW);
} else {
// 如果没有关联的便签,显示默认提示信息
snippet = context.getResources().getString(R.string.widget_havenot_content);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
}
// 确保关闭Cursor以防止资源泄漏
if (c != null) {
c.close();
}
// 创建并配置RemoteViews
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
// 根据是否为隐私模式设置不同的PendingIntent和显示内容
PendingIntent pendingIntent = null;
if (privacyMode) {
// 隐私模式下显示通用提示,点击打开便签列表
rv.setTextViewText(R.id.widget_text,
context.getString(R.string.widget_under_visit_mode));
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
} else {
// 正常模式下显示便签内容,点击打开便签编辑界面
rv.setTextViewText(R.id.widget_text, snippet);
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
// 设置点击事件并更新小部件
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
}
}
/**
* 获取指定背景ID对应的资源ID。
* 由子类实现,根据不同的小部件样式返回不同的背景资源。
*
* @param bgId 背景ID
* @return 背景资源ID
*/
protected abstract int getBgResourceId(int bgId);
/**
* 获取小部件的布局资源ID。
* 由子类实现,返回特定尺寸小部件的布局。
*
* @return 布局资源ID
*/
protected abstract int getLayoutId();
/**
* 获取小部件类型。
* 由子类实现,返回小部件的类型常量。
*
* @return 小部件类型
*/
protected abstract int getWidgetType();
}