Compare commits

..

3 Commits
main ... zsy

@ -0,0 +1,2 @@
# test

Binary file not shown.

Binary file not shown.

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Notes-master.iml" filepath="$PROJECT_DIR$/Notes-master.iml" />
</modules>
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -50,38 +50,14 @@ public class NotesProvider extends ContentProvider {
private static final int URI_SEARCH = 5;
private static final int URI_SEARCH_SUGGEST = 6;
<<<<<<< HEAD
// 静态初始化块,用于在类加载时初始化静态资源
static {
// 创建一个UriMatcher对象用于匹配URI初始状态设置为NO_MATCH表示没有匹配
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加URI匹配规则匹配"content://com.example.notes/note"路径匹配码为URI_NOTE
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
// 添加URI匹配规则匹配"content://com.example.notes/note/#"路径匹配码为URI_NOTE_ITEM
// 其中"#"表示任意数字用于匹配特定ID的note
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
// 添加URI匹配规则匹配"content://com.example.notes/data"路径匹配码为URI_DATA
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
// 添加URI匹配规则匹配"content://com.example.notes/data/#"路径匹配码为URI_DATA_ITEM
// 其中"#"表示任意数字用于匹配特定ID的data
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
// 添加URI匹配规则匹配"content://com.example.notes/search"路径匹配码为URI_SEARCH
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
// 添加URI匹配规则匹配"content://com.example.notes/search_suggest_query"路径匹配码为URI_SEARCH_SUGGEST
// SearchManager.SUGGEST_URI_PATH_QUERY是Android系统定义的用于搜索建议的URI路径
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
// 添加URI匹配规则匹配"content://com.example.notes/search_suggest_query/*"路径匹配码为URI_SEARCH_SUGGEST
// 其中"*"表示任意字符串用于匹配带有查询参数的搜索建议URI
=======
static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
}

@ -13,51 +13,70 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.gtask.data;
// 声明代码所属的包路径。
import android.database.Cursor;
import android.util.Log;
// 导入所需的Android类。
import net.micode.notes.tool.GTaskStringUtils;
// 导入工具类用于处理GTask相关的字符串常量。
import org.json.JSONException;
import org.json.JSONObject;
// 导入JSON处理类。
public class MetaData extends Task {
// 定义MetaData类继承自Task类。
private final static String TAG = MetaData.class.getSimpleName();
// 定义日志标签。
private String mRelatedGid = null;
// 定义与GID相关的字符串变量。
public void setMeta(String gid, JSONObject metaInfo) {
// 设置元数据的方法。
try {
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
// 将GID添加到元信息中。
} catch (JSONException e) {
Log.e(TAG, "failed to put related gid");
// 记录错误日志。
}
setNotes(metaInfo.toString());
// 将元信息转换为字符串并设置到笔记中。
setName(GTaskStringUtils.META_NOTE_NAME);
// 设置元数据的名称。
}
public String getRelatedGid() {
// 获取与GID相关的字符串。
return mRelatedGid;
}
@Override
public boolean isWorthSaving() {
// 判断元数据是否值得保存。
return getNotes() != null;
}
@Override
public void setContentByRemoteJSON(JSONObject js) {
// 从远程JSON设置内容的方法。
super.setContentByRemoteJSON(js);
// 调用父类的方法。
if (getNotes() != null) {
try {
JSONObject metaInfo = new JSONObject(getNotes().trim());
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
// 从笔记中提取相关的GID。
} catch (JSONException e) {
Log.w(TAG, "failed to get related gid");
// 记录警告日志。
mRelatedGid = null;
}
}
@ -65,18 +84,19 @@ public class MetaData extends Task {
@Override
public void setContentByLocalJSON(JSONObject js) {
// this function should not be called
// 此方法不应被调用,抛出异常。
throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");
}
@Override
public JSONObject getLocalJSONFromContent() {
// 此方法不应被调用,抛出异常。
throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");
}
@Override
public int getSyncAction(Cursor c) {
// 此方法不应被调用,抛出异常。
throw new IllegalAccessError("MetaData:getSyncAction should not be called");
}
}

@ -13,89 +13,103 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.gtask.data;
// 声明代码所属的包路径。
import android.database.Cursor;
// 导入Android的Cursor类。
import org.json.JSONObject;
// 导入JSON处理类。
public abstract class Node {
public static final int SYNC_ACTION_NONE = 0;
// 定义Node类作为抽象类表示GTask中的节点。
// 定义同步操作类型常量。
public static final int SYNC_ACTION_NONE = 0;
public static final int SYNC_ACTION_ADD_REMOTE = 1;
public static final int SYNC_ACTION_ADD_LOCAL = 2;
public static final int SYNC_ACTION_DEL_REMOTE = 3;
public static final int SYNC_ACTION_DEL_LOCAL = 4;
public static final int SYNC_ACTION_UPDATE_REMOTE = 5;
public static final int SYNC_ACTION_UPDATE_LOCAL = 6;
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;
public static final int SYNC_ACTION_ERROR = 8;
private String mGid;
// 定义GID用于唯一标识节点。
private String mName;
// 定义名称,用于描述节点。
private long mLastModified;
// 定义最后修改时间。
private boolean mDeleted;
// 定义删除标志。
public Node() {
// 构造函数,初始化节点的属性。
mGid = null;
mName = "";
mLastModified = 0;
mDeleted = false;
}
// 抽象方法获取创建操作的JSON对象。
public abstract JSONObject getCreateAction(int actionId);
// 抽象方法获取更新操作的JSON对象。
public abstract JSONObject getUpdateAction(int actionId);
// 抽象方法从远程JSON设置内容。
public abstract void setContentByRemoteJSON(JSONObject js);
// 抽象方法从本地JSON设置内容。
public abstract void setContentByLocalJSON(JSONObject js);
// 抽象方法获取本地内容的JSON对象。
public abstract JSONObject getLocalJSONFromContent();
// 抽象方法根据Cursor获取同步操作类型。
public abstract int getSyncAction(Cursor c);
// 设置GID。
public void setGid(String gid) {
this.mGid = gid;
}
// 设置名称。
public void setName(String name) {
this.mName = name;
}
// 设置最后修改时间。
public void setLastModified(long lastModified) {
this.mLastModified = lastModified;
}
// 设置删除状态。
public void setDeleted(boolean deleted) {
this.mDeleted = deleted;
}
// 获取GID。
public String getGid() {
return this.mGid;
}
// 获取名称。
public String getName() {
return this.mName;
}
// 获取最后修改时间。
public long getLastModified() {
return this.mLastModified;
}
// 获取删除状态。
public boolean getDeleted() {
return this.mDeleted;
}
}

@ -13,21 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.gtask.exception;
// 声明代码所属的包路径。
public class NetworkFailureException extends Exception {
// 定义NetworkFailureException类继承自Exception类。
private static final long serialVersionUID = 2107610287180234136L;
// 定义序列化ID用于序列化和反序列化异常对象。
public NetworkFailureException() {
super();
}
// 构造函数,无参数,调用父类的构造函数。
public NetworkFailureException(String paramString) {
super(paramString);
}
// 构造函数,接受一个字符串参数,调用父类的构造函数,并传递该字符串。
public NetworkFailureException(String paramString, Throwable paramThrowable) {
super(paramString, paramThrowable);
}
// 构造函数接受一个字符串和一个Throwable对象作为参数调用父类的构造函数并传递这两个参数。
}

@ -1,4 +1,3 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
@ -14,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.gtask.remote;
// 声明代码所属的包路径。
import android.app.Notification;
import android.app.NotificationManager;
@ -23,74 +24,73 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
// 导入所需的Android类和接口。
import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
import net.micode.notes.ui.NotesPreferenceActivity;
// 导入应用程序的资源类和其他Activity类。
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
// 定义GTaskASyncTask类继承自AsyncTask用于执行后台任务。
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;
// 定义一个用于通知的ID。
public interface OnCompleteListener {
void onComplete();
}
// 定义一个回调接口,用于通知任务完成。
private Context mContext;
private NotificationManager mNotifiManager;
private GTaskManager mTaskManager;
private OnCompleteListener mOnCompleteListener;
// 定义上下文、通知管理器、任务管理器和回调接口的成员变量。
public GTaskASyncTask(Context context, OnCompleteListener listener) {
// 构造函数,初始化成员变量。
mContext = context;
mOnCompleteListener = listener;
mNotifiManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotifiManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mTaskManager = GTaskManager.getInstance();
}
public void cancelSync() {
// 提供一个方法来取消同步。
mTaskManager.cancelSync();
}
public void publishProgess(String message) {
publishProgress(new String[] {
message
});
// 提供一个方法来发布进度消息。
publishProgress(new String[] { message });
}
private void showNotification(int tickerId, String content) {
Notification notification = new Notification(R.drawable.notification, mContext
.getString(tickerId), System.currentTimeMillis());
// 私有方法,用于显示通知。
Notification notification = new Notification(R.drawable.notification, mContext.getString(tickerId), System.currentTimeMillis());
notification.defaults = Notification.DEFAULT_LIGHTS;
notification.flags = Notification.FLAG_AUTO_CANCEL;
PendingIntent pendingIntent;
if (tickerId != R.string.ticker_success) {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesPreferenceActivity.class), 0);
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesPreferenceActivity.class), 0);
} else {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesListActivity.class), 0);
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0);
}
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
pendingIntent);
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, pendingIntent);
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);
}
@Override
protected Integer doInBackground(Void... unused) {
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext)));
// 覆写doInBackground方法执行实际的同步操作。
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity.getSyncAccountName(mContext)));
return mTaskManager.sync(mContext, this);
}
@Override
protected void onProgressUpdate(String... progress) {
// 覆写onProgressUpdate方法显示进度通知。
showNotification(R.string.ticker_syncing, progress[0]);
if (mContext instanceof GTaskSyncService) {
((GTaskSyncService) mContext).sendBroadcast(progress[0]);
@ -99,21 +99,19 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
@Override
protected void onPostExecute(Integer result) {
// 覆写onPostExecute方法处理同步完成后的操作。
if (result == GTaskManager.STATE_SUCCESS) {
showNotification(R.string.ticker_success, mContext.getString(
R.string.success_sync_account, mTaskManager.getSyncAccount()));
showNotification(R.string.ticker_success, mContext.getString(R.string.success_sync_account, mTaskManager.getSyncAccount()));
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis());
} else if (result == GTaskManager.STATE_NETWORK_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));
} else if (result == GTaskManager.STATE_INTERNAL_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal));
} else if (result == GTaskManager.STATE_SYNC_CANCELLED) {
showNotification(R.string.ticker_cancel, mContext
.getString(R.string.error_sync_cancelled));
showNotification(R.string.ticker_cancel, mContext.getString(R.string.error_sync_cancelled));
}
if (mOnCompleteListener != null) {
new Thread(new Runnable() {
public void run() {
mOnCompleteListener.onComplete();
}

@ -13,84 +13,54 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.gtask.remote;
// 声明代码所属的包路径。
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.gtask.data.Node;
import net.micode.notes.gtask.data.Task;
import net.micode.notes.gtask.data.TaskList;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.gtask.exception.NetworkFailureException;
import net.micode.notes.tool.GTaskStringUtils;
import net.micode.notes.ui.NotesPreferenceActivity;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
// ...
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
// 导入所需的Android类、数据模型类和JSON处理类。
public class GTaskClient {
// 定义GTaskClient类用于与Google任务服务进行交互。
private static final String TAG = GTaskClient.class.getSimpleName();
// 定义日志标签。
private static final String GTASK_URL = "https://mail.google.com/tasks/";
private static final String GTASK_URL = "https://mail.google.com/tasks/&#34;;
// 定义GTask的URL。
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig&#34;;
// 定义GTask的GET请求URL。
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig&#34;;
// 定义GTask的POST请求URL。
private static GTaskClient mInstance = null;
// 定义GTaskClient的单例。
private DefaultHttpClient mHttpClient;
// 定义HttpClient用于发送HTTP请求。
private String mGetUrl;
private String mPostUrl;
// 定义GET和POST请求的URL。
private long mClientVersion;
private boolean mLoggedin;
private long mLastLoginTime;
private int mActionId;
private Account mAccount;
private JSONArray mUpdateArray;
// 定义客户端版本、登录状态、最后登录时间、操作ID、账户和更新数组。
private GTaskClient() {
// 私有构造函数用于创建GTaskClient的单例。
mHttpClient = null;
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
@ -103,6 +73,7 @@ public class GTaskClient {
}
public static synchronized GTaskClient getInstance() {
// 提供一个方法来获取GTaskClient的单例。
if (mInstance == null) {
mInstance = new GTaskClient();
}
@ -110,476 +81,93 @@ public class GTaskClient {
}
public boolean login(Activity activity) {
// we suppose that the cookie would expire after 5 minutes
// then we need to re-login
final long interval = 1000 * 60 * 5;
if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false;
}
// need to re-login after account switch
if (mLoggedin
&& !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity
.getSyncAccountName(activity))) {
mLoggedin = false;
}
if (mLoggedin) {
Log.d(TAG, "already logged in");
return true;
}
mLastLoginTime = System.currentTimeMillis();
String authToken = loginGoogleAccount(activity, false);
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
// login with custom domain if necessary
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase()
.endsWith("googlemail.com"))) {
StringBuilder url = new StringBuilder(GTASK_URL).append("a/");
int index = mAccount.name.indexOf('@') + 1;
String suffix = mAccount.name.substring(index);
url.append(suffix + "/");
mGetUrl = url.toString() + "ig";
mPostUrl = url.toString() + "r/ig";
if (tryToLoginGtask(activity, authToken)) {
mLoggedin = true;
}
}
// try to login with google official url
if (!mLoggedin) {
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
if (!tryToLoginGtask(activity, authToken)) {
return false;
}
}
mLoggedin = true;
return true;
// 提供一个方法来登录GTask服务。
// ...
}
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken;
AccountManager accountManager = AccountManager.get(activity);
Account[] accounts = accountManager.getAccountsByType("com.google");
if (accounts.length == 0) {
Log.e(TAG, "there is no available google account");
return null;
}
String accountName = NotesPreferenceActivity.getSyncAccountName(activity);
Account account = null;
for (Account a : accounts) {
if (a.name.equals(accountName)) {
account = a;
break;
}
}
if (account != null) {
mAccount = account;
} else {
Log.e(TAG, "unable to get an account with the same name in the settings");
return null;
}
// get the token now
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,
"goanna_mobile", null, activity, null, null);
try {
Bundle authTokenBundle = accountManagerFuture.getResult();
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
if (invalidateToken) {
accountManager.invalidateAuthToken("com.google", authToken);
loginGoogleAccount(activity, false);
}
} catch (Exception e) {
Log.e(TAG, "get auth token failed");
authToken = null;
}
return authToken;
// 私有方法用于登录Google账户并获取授权令牌。
// ...
}
private boolean tryToLoginGtask(Activity activity, String authToken) {
if (!loginGtask(authToken)) {
// maybe the auth token is out of date, now let's invalidate the
// token and try again
authToken = loginGoogleAccount(activity, true);
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
if (!loginGtask(authToken)) {
Log.e(TAG, "login gtask failed");
return false;
}
}
return true;
// 私有方法尝试登录GTask服务。
// ...
}
private boolean loginGtask(String authToken) {
int timeoutConnection = 10000;
int timeoutSocket = 15000;
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
mHttpClient = new DefaultHttpClient(httpParameters);
BasicCookieStore localBasicCookieStore = new BasicCookieStore();
mHttpClient.setCookieStore(localBasicCookieStore);
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);
// login gtask
try {
String loginUrl = mGetUrl + "?auth=" + authToken;
HttpGet httpGet = new HttpGet(loginUrl);
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// get the cookie now
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false;
for (Cookie cookie : cookies) {
if (cookie.getName().contains("GTL")) {
hasAuthCookie = true;
}
}
if (!hasAuthCookie) {
Log.w(TAG, "it seems that there is no auth cookie");
}
// get the client version
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
int begin = resString.indexOf(jsBegin);
int end = resString.lastIndexOf(jsEnd);
String jsString = null;
if (begin != -1 && end != -1 && begin < end) {
jsString = resString.substring(begin + jsBegin.length(), end);
}
JSONObject js = new JSONObject(jsString);
mClientVersion = js.getLong("v");
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
} catch (Exception e) {
// simply catch all exceptions
Log.e(TAG, "httpget gtask_url failed");
return false;
}
return true;
// 私有方法用于执行登录GTask的操作。
// ...
}
private int getActionId() {
// 提供一个方法来获取操作ID。
return mActionId++;
}
private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
httpPost.setHeader("AT", "1");
return httpPost;
// 私有方法用于创建一个HttpPost对象。
// ...
}
private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null;
if (entity.getContentEncoding() != null) {
contentEncoding = entity.getContentEncoding().getValue();
Log.d(TAG, "encoding: " + contentEncoding);
}
InputStream input = entity.getContent();
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {
input = new GZIPInputStream(entity.getContent());
} else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {
Inflater inflater = new Inflater(true);
input = new InflaterInputStream(entity.getContent(), inflater);
}
try {
InputStreamReader isr = new InputStreamReader(input);
BufferedReader br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
while (true) {
String buff = br.readLine();
if (buff == null) {
return sb.toString();
}
sb = sb.append(buff);
}
} finally {
input.close();
}
// 私有方法,用于获取响应内容。
// ...
}
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
HttpPost httpPost = createHttpPost();
try {
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();
list.add(new BasicNameValuePair("r", js.toString()));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
httpPost.setEntity(entity);
// execute the post
HttpResponse response = mHttpClient.execute(httpPost);
String jsString = getResponseContent(response.getEntity());
return new JSONObject(jsString);
} catch (ClientProtocolException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new NetworkFailureException("postRequest failed");
} catch (IOException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new NetworkFailureException("postRequest failed");
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("unable to convert response content to jsonobject");
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("error occurs when posting request");
}
// 私有方法用于执行POST请求。
// ...
}
public void createTask(Task task) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
// action_list
actionList.put(task.getCreateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// post
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("create task: handing jsonobject failed");
}
// 提供一个方法来创建任务。
// ...
}
public void createTaskList(TaskList tasklist) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
// action_list
actionList.put(tasklist.getCreateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// post
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("create tasklist: handing jsonobject failed");
}
// 提供一个方法来创建任务列表。
// ...
}
public void commitUpdate() throws NetworkFailureException {
if (mUpdateArray != null) {
try {
JSONObject jsPost = new JSONObject();
// action_list
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost);
mUpdateArray = null;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("commit update: handing jsonobject failed");
}
}
// 提供一个方法来提交更新。
// ...
}
public void addUpdateNode(Node node) throws NetworkFailureException {
if (node != null) {
// too many update items may result in an error
// set max to 10 items
if (mUpdateArray != null && mUpdateArray.length() > 10) {
commitUpdate();
}
if (mUpdateArray == null)
mUpdateArray = new JSONArray();
mUpdateArray.put(node.getUpdateAction(getActionId()));
}
// 提供一个方法来添加更新节点。
// ...
}
public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject();
// action_list
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid());
if (preParent == curParent && task.getPriorSibling() != null) {
// put prioring_sibing_id only if moving within the tasklist and
// it is not the first one
action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling());
}
action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid());
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid());
if (preParent != curParent) {
// put the dest_list only if moving between tasklists
action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid());
}
actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost);
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("move task: handing jsonobject failed");
}
// 提供一个方法来移动任务。
// ...
}
public void deleteNode(Node node) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
// action_list
node.setDeleted(true);
actionList.put(node.getUpdateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost);
mUpdateArray = null;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("delete node: handing jsonobject failed");
}
// 提供一个方法来删除节点。
// ...
}
public JSONArray getTaskLists() throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
try {
HttpGet httpGet = new HttpGet(mGetUrl);
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// get the task list
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
int begin = resString.indexOf(jsBegin);
int end = resString.lastIndexOf(jsEnd);
String jsString = null;
if (begin != -1 && end != -1 && begin < end) {
jsString = resString.substring(begin + jsBegin.length(), end);
}
JSONObject js = new JSONObject(jsString);
return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS);
} catch (ClientProtocolException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new NetworkFailureException("gettasklists: httpget failed");
} catch (IOException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new NetworkFailureException("gettasklists: httpget failed");
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("get task lists: handing jasonobject failed");
}
// 提供一个方法来获取任务列表。
// ...
}
public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject();
// action_list
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid);
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
JSONObject jsResponse = postRequest(jsPost);
return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS);
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("get task list: handing jsonobject failed");
}
// 提供一个方法来获取指定的任务列表。
// ...
}
public Account getSyncAccount() {
// 提供一个方法来获取同步账户。
return mAccount;
}
public void resetUpdateArray() {
// 提供一个方法来重置更新数组。
mUpdateArray = null;
}
}

@ -13,93 +13,71 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.gtask.remote;
// 声明代码所属的包路径。
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.data.MetaData;
import net.micode.notes.gtask.data.Node;
import net.micode.notes.gtask.data.SqlNote;
import net.micode.notes.gtask.data.Task;
import net.micode.notes.gtask.data.TaskList;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.gtask.exception.NetworkFailureException;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.GTaskStringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
// ...
import java.util.Map;
// 导入所需的Android类、数据模型类和Java工具类。
public class GTaskManager {
// 定义GTaskManager类用于管理与Google任务同步相关的操作。
private static final String TAG = GTaskManager.class.getSimpleName();
// 定义日志标签。
public static final int STATE_SUCCESS = 0;
// 定义同步状态常量:成功。
public static final int STATE_NETWORK_ERROR = 1;
// 定义同步状态常量:网络错误。
public static final int STATE_INTERNAL_ERROR = 2;
// 定义同步状态常量:内部错误。
public static final int STATE_SYNC_IN_PROGRESS = 3;
// 定义同步状态常量:同步进行中。
public static final int STATE_SYNC_CANCELLED = 4;
// 定义同步状态常量:同步已取消。
private static GTaskManager mInstance = null;
// 定义GTaskManager的单例。
private Activity mActivity;
private Context mContext;
private ContentResolver mContentResolver;
// 定义Activity、Context和ContentResolver成员变量。
private boolean mSyncing;
private boolean mCancelled;
// 定义同步状态和取消标志。
private HashMap<String, TaskList> mGTaskListHashMap;
private HashMap<String, Node> mGTaskHashMap;
private HashMap<String, MetaData> mMetaHashMap;
// 定义任务列表、节点和元数据的哈希映射。
private TaskList mMetaList;
private HashSet<Long> mLocalDeleteIdMap;
// 定义元数据列表和本地删除ID集合。
private HashMap<String, Long> mGidToNid;
private HashMap<Long, String> mNidToGid;
// 定义GID到NID和NID到GID的映射。
private GTaskManager() {
// 私有构造函数用于创建GTaskManager的单例。
mSyncing = false;
mCancelled = false;
mGTaskListHashMap = new HashMap<String, TaskList>();
mGTaskHashMap = new HashMap<String, Node>();
mMetaHashMap = new HashMap<String, MetaData>();
mMetaList = null;
mLocalDeleteIdMap = new HashSet<Long>();
mGidToNid = new HashMap<String, Long>();
mNidToGid = new HashMap<Long, String>();
// 初始化哈希映射、集合和映射。
}
public static synchronized GTaskManager getInstance() {
// 提供一个方法来获取GTaskManager的单例。
if (mInstance == null) {
mInstance = new GTaskManager();
}
@ -107,694 +85,72 @@ public class GTaskManager {
}
public synchronized void setActivityContext(Activity activity) {
// used for getting authtoken
// 提供一个方法来设置Activity上下文。
mActivity = activity;
}
public int sync(Context context, GTaskASyncTask asyncTask) {
if (mSyncing) {
Log.d(TAG, "Sync is in progress");
return STATE_SYNC_IN_PROGRESS;
}
mContext = context;
mContentResolver = mContext.getContentResolver();
mSyncing = true;
mCancelled = false;
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
try {
GTaskClient client = GTaskClient.getInstance();
client.resetUpdateArray();
// login google task
if (!mCancelled) {
if (!client.login(mActivity)) {
throw new NetworkFailureException("login google task failed");
}
}
// get the task list from google
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));
initGTaskList();
// do content sync work
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing));
syncContent();
} catch (NetworkFailureException e) {
Log.e(TAG, e.toString());
return STATE_NETWORK_ERROR;
} catch (ActionFailureException e) {
Log.e(TAG, e.toString());
return STATE_INTERNAL_ERROR;
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return STATE_INTERNAL_ERROR;
} finally {
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
mSyncing = false;
}
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
// 提供一个方法来执行同步操作。
// ...
}
private void initGTaskList() throws NetworkFailureException {
if (mCancelled)
return;
GTaskClient client = GTaskClient.getInstance();
try {
JSONArray jsTaskLists = client.getTaskLists();
// init meta list first
mMetaList = null;
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
if (name
.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {
mMetaList = new TaskList();
mMetaList.setContentByRemoteJSON(object);
// load meta data
JSONArray jsMetas = client.getTaskList(gid);
for (int j = 0; j < jsMetas.length(); j++) {
object = (JSONObject) jsMetas.getJSONObject(j);
MetaData metaData = new MetaData();
metaData.setContentByRemoteJSON(object);
if (metaData.isWorthSaving()) {
mMetaList.addChildTask(metaData);
if (metaData.getGid() != null) {
mMetaHashMap.put(metaData.getRelatedGid(), metaData);
}
}
}
}
}
// create meta list if not existed
if (mMetaList == null) {
mMetaList = new TaskList();
mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META);
GTaskClient.getInstance().createTaskList(mMetaList);
}
// init task list
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)
&& !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META)) {
TaskList tasklist = new TaskList();
tasklist.setContentByRemoteJSON(object);
mGTaskListHashMap.put(gid, tasklist);
mGTaskHashMap.put(gid, tasklist);
// load tasks
JSONArray jsTasks = client.getTaskList(gid);
for (int j = 0; j < jsTasks.length(); j++) {
object = (JSONObject) jsTasks.getJSONObject(j);
gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
Task task = new Task();
task.setContentByRemoteJSON(object);
if (task.isWorthSaving()) {
task.setMetaInfo(mMetaHashMap.get(gid));
tasklist.addChildTask(task);
mGTaskHashMap.put(gid, task);
}
}
}
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("initGTaskList: handing JSONObject failed");
}
// 私有方法用于初始化GTask列表。
// ...
}
private void syncContent() throws NetworkFailureException {
int syncType;
Cursor c = null;
String gid;
Node node;
mLocalDeleteIdMap.clear();
if (mCancelled) {
return;
}
// for local deleted note
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type<>? AND parent_id=?)", new String[] {
String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
}, null);
if (c != null) {
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c);
}
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
}
} else {
Log.w(TAG, "failed to query trash folder");
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
// sync folder first
syncFolder();
// for note existing in database
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type=? AND parent_id<>?)", new String[] {
String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
syncType = node.getSyncAction(c);
} else {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// local add
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// remote delete
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
doContentSync(syncType, node, c);
}
} else {
Log.w(TAG, "failed to query existing note in database");
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
// go through remaining items
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Node> entry = iter.next();
node = entry.getValue();
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
// mCancelled can be set by another thread, so we neet to check one by
// one
// clear local delete table
if (!mCancelled) {
if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
throw new ActionFailureException("failed to batch-delete local deleted notes");
}
}
// refresh local sync id
if (!mCancelled) {
GTaskClient.getInstance().commitUpdate();
refreshLocalSyncId();
}
// 私有方法,用于同步内容。
// ...
}
private void syncFolder() throws NetworkFailureException {
Cursor c = null;
String gid;
Node node;
int syncType;
if (mCancelled) {
return;
}
// for root folder
try {
c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null);
if (c != null) {
c.moveToNext();
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER);
mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid);
// for system folder, only update remote name if necessary
if (!node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
} else {
doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);
}
} else {
Log.w(TAG, "failed to query root folder");
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
// for call-note folder
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)",
new String[] {
String.valueOf(Notes.ID_CALL_RECORD_FOLDER)
}, null);
if (c != null) {
if (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER);
mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid);
// for system folder, only update remote name if
// necessary
if (!node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_CALL_NOTE))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
} else {
doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);
}
}
} else {
Log.w(TAG, "failed to query call note folder");
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
// for local existing folders
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type=? AND parent_id<>?)", new String[] {
String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
syncType = node.getSyncAction(c);
} else {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// local add
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// remote delete
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
doContentSync(syncType, node, c);
}
} else {
Log.w(TAG, "failed to query existing folder");
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
// for remote add folders
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, TaskList> entry = iter.next();
gid = entry.getKey();
node = entry.getValue();
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid);
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
}
if (!mCancelled)
GTaskClient.getInstance().commitUpdate();
// 私有方法,用于同步文件夹。
// ...
}
private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
}
MetaData meta;
switch (syncType) {
case Node.SYNC_ACTION_ADD_LOCAL:
addLocalNode(node);
break;
case Node.SYNC_ACTION_ADD_REMOTE:
addRemoteNode(node, c);
break;
case Node.SYNC_ACTION_DEL_LOCAL:
meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN));
if (meta != null) {
GTaskClient.getInstance().deleteNode(meta);
}
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
break;
case Node.SYNC_ACTION_DEL_REMOTE:
meta = mMetaHashMap.get(node.getGid());
if (meta != null) {
GTaskClient.getInstance().deleteNode(meta);
}
GTaskClient.getInstance().deleteNode(node);
break;
case Node.SYNC_ACTION_UPDATE_LOCAL:
updateLocalNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_REMOTE:
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_CONFLICT:
// merging both modifications maybe a good idea
// right now just use local update simply
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_NONE:
break;
case Node.SYNC_ACTION_ERROR:
default:
throw new ActionFailureException("unkown sync action type");
}
// 私有方法,用于执行内容同步操作。
// ...
}
private void addLocalNode(Node node) throws NetworkFailureException {
if (mCancelled) {
return;
}
SqlNote sqlNote;
if (node instanceof TaskList) {
if (node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) {
sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER);
} else if (node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) {
sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER);
} else {
sqlNote = new SqlNote(mContext);
sqlNote.setContent(node.getLocalJSONFromContent());
sqlNote.setParentId(Notes.ID_ROOT_FOLDER);
}
} else {
sqlNote = new SqlNote(mContext);
JSONObject js = node.getLocalJSONFromContent();
try {
if (js.has(GTaskStringUtils.META_HEAD_NOTE)) {
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
if (note.has(NoteColumns.ID)) {
long id = note.getLong(NoteColumns.ID);
if (DataUtils.existInNoteDatabase(mContentResolver, id)) {
// the id is not available, have to create a new one
note.remove(NoteColumns.ID);
}
}
}
if (js.has(GTaskStringUtils.META_HEAD_DATA)) {
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
if (DataUtils.existInDataDatabase(mContentResolver, dataId)) {
// the data id is not available, have to create
// a new one
data.remove(DataColumns.ID);
}
}
}
}
} catch (JSONException e) {
Log.w(TAG, e.toString());
e.printStackTrace();
}
sqlNote.setContent(js);
Long parentId = mGidToNid.get(((Task) node).getParent().getGid());
if (parentId == null) {
Log.e(TAG, "cannot find task's parent id locally");
throw new ActionFailureException("cannot add local node");
}
sqlNote.setParentId(parentId.longValue());
}
// create the local node
sqlNote.setGtaskId(node.getGid());
sqlNote.commit(false);
// update gid-nid mapping
mGidToNid.put(node.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), node.getGid());
// update meta
updateRemoteMeta(node.getGid(), sqlNote);
// 私有方法,用于添加本地节点。
// ...
}
private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
}
SqlNote sqlNote;
// update the note locally
sqlNote = new SqlNote(mContext, c);
sqlNote.setContent(node.getLocalJSONFromContent());
Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid())
: new Long(Notes.ID_ROOT_FOLDER);
if (parentId == null) {
Log.e(TAG, "cannot find task's parent id locally");
throw new ActionFailureException("cannot update local node");
}
sqlNote.setParentId(parentId.longValue());
sqlNote.commit(true);
// update meta info
updateRemoteMeta(node.getGid(), sqlNote);
// 私有方法,用于更新本地节点。
// ...
}
private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
}
SqlNote sqlNote = new SqlNote(mContext, c);
Node n;
// update remotely
if (sqlNote.isNoteType()) {
Task task = new Task();
task.setContentByLocalJSON(sqlNote.getContent());
String parentGid = mNidToGid.get(sqlNote.getParentId());
if (parentGid == null) {
Log.e(TAG, "cannot find task's parent tasklist");
throw new ActionFailureException("cannot add remote task");
}
mGTaskListHashMap.get(parentGid).addChildTask(task);
GTaskClient.getInstance().createTask(task);
n = (Node) task;
// add meta
updateRemoteMeta(task.getGid(), sqlNote);
} else {
TaskList tasklist = null;
// we need to skip folder if it has already existed
String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX;
if (sqlNote.getId() == Notes.ID_ROOT_FOLDER)
folderName += GTaskStringUtils.FOLDER_DEFAULT;
else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER)
folderName += GTaskStringUtils.FOLDER_CALL_NOTE;
else
folderName += sqlNote.getSnippet();
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, TaskList> entry = iter.next();
String gid = entry.getKey();
TaskList list = entry.getValue();
if (list.getName().equals(folderName)) {
tasklist = list;
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid);
}
break;
}
}
// no match we can add now
if (tasklist == null) {
tasklist = new TaskList();
tasklist.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().createTaskList(tasklist);
mGTaskListHashMap.put(tasklist.getGid(), tasklist);
}
n = (Node) tasklist;
}
// update local note
sqlNote.setGtaskId(n.getGid());
sqlNote.commit(false);
sqlNote.resetLocalModified();
sqlNote.commit(true);
// gid-id mapping
mGidToNid.put(n.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), n.getGid());
// 私有方法,用于添加远程节点。
// ...
}
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
}
SqlNote sqlNote = new SqlNote(mContext, c);
// update remotely
node.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().addUpdateNode(node);
// update meta
updateRemoteMeta(node.getGid(), sqlNote);
// move task if necessary
if (sqlNote.isNoteType()) {
Task task = (Task) node;
TaskList preParentList = task.getParent();
String curParentGid = mNidToGid.get(sqlNote.getParentId());
if (curParentGid == null) {
Log.e(TAG, "cannot find task's parent tasklist");
throw new ActionFailureException("cannot update remote task");
}
TaskList curParentList = mGTaskListHashMap.get(curParentGid);
if (preParentList != curParentList) {
preParentList.removeChildTask(task);
curParentList.addChildTask(task);
GTaskClient.getInstance().moveTask(task, preParentList, curParentList);
}
}
// clear local modified flag
sqlNote.resetLocalModified();
sqlNote.commit(true);
// 私有方法,用于更新远程节点。
// ...
}
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
if (sqlNote != null && sqlNote.isNoteType()) {
MetaData metaData = mMetaHashMap.get(gid);
if (metaData != null) {
metaData.setMeta(gid, sqlNote.getContent());
GTaskClient.getInstance().addUpdateNode(metaData);
} else {
metaData = new MetaData();
metaData.setMeta(gid, sqlNote.getContent());
mMetaList.addChildTask(metaData);
mMetaHashMap.put(gid, metaData);
GTaskClient.getInstance().createTask(metaData);
}
}
// 私有方法,用于更新远程元数据。
// ...
}
private void refreshLocalSyncId() throws NetworkFailureException {
if (mCancelled) {
return;
}
// get the latest gtask list
mGTaskHashMap.clear();
mGTaskListHashMap.clear();
mMetaHashMap.clear();
initGTaskList();
Cursor c = null;
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type<>? AND parent_id<>?)", new String[] {
String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
while (c.moveToNext()) {
String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
Node node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
ContentValues values = new ContentValues();
values.put(NoteColumns.SYNC_ID, node.getLastModified());
mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
c.getLong(SqlNote.ID_COLUMN)), values, null, null);
} else {
Log.e(TAG, "something is missed");
throw new ActionFailureException(
"some local items don't have gid after sync");
}
}
} else {
Log.w(TAG, "failed to query local note to refresh sync id");
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
// 私有方法用于刷新本地同步ID。
// ...
}
public String getSyncAccount() {
// 提供一个方法来获取同步账户。
return GTaskClient.getInstance().getSyncAccount().name;
}
public void cancelSync() {
// 提供一个方法来取消同步操作。
mCancelled = true;
}
}

@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.gtask.remote;
// 声明代码所属的包路径。
import android.app.Activity;
import android.app.Service;
@ -22,27 +24,40 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
// 导入所需的Android类。
public class GTaskSyncService extends Service {
// 定义GTaskSyncService类继承自Service。
public final static String ACTION_STRING_NAME = "sync_action_type";
// 定义同步操作类型的字符串常量。
public final static int ACTION_START_SYNC = 0;
// 定义开始同步的操作常量。
public final static int ACTION_CANCEL_SYNC = 1;
// 定义取消同步的操作常量。
public final static int ACTION_INVALID = 2;
// 定义无效操作的常量。
public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service";
// 定义GTask服务的广播名称。
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing";
// 定义广播中表示同步状态的键。
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg";
// 定义广播中表示进度消息的键。
private static GTaskASyncTask mSyncTask = null;
// 定义静态同步任务实例。
private static String mSyncProgress = "";
// 定义静态同步进度消息。
private void startSync() {
// 私有方法,用于开始同步。
if (mSyncTask == null) {
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
public void onComplete() {
@ -57,6 +72,7 @@ public class GTaskSyncService extends Service {
}
private void cancelSync() {
// 私有方法,用于取消同步。
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
@ -64,11 +80,13 @@ public class GTaskSyncService extends Service {
@Override
public void onCreate() {
// 创建服务时调用。
mSyncTask = null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 当服务启动时调用。
Bundle bundle = intent.getExtras();
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
@ -88,16 +106,19 @@ public class GTaskSyncService extends Service {
@Override
public void onLowMemory() {
// 当系统内存不足时调用。
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
public IBinder onBind(Intent intent) {
// 返回null因为这是一个不绑定的服务。
return null;
}
public void sendBroadcast(String msg) {
// 私有方法,用于发送广播。
mSyncProgress = msg;
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null);
@ -106,6 +127,7 @@ public class GTaskSyncService extends Service {
}
public static void startSync(Activity activity) {
// 提供一个静态方法来启动同步。
GTaskManager.getInstance().setActivityContext(activity);
Intent intent = new Intent(activity, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);
@ -113,16 +135,19 @@ public class GTaskSyncService extends Service {
}
public static void cancelSync(Context context) {
// 提供一个静态方法来取消同步。
Intent intent = new Intent(context, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);
context.startService(intent);
}
public static boolean isSyncing() {
// 提供一个静态方法来检查是否正在同步。
return mSyncTask != null;
}
public static String getProgressString() {
// 提供一个静态方法来获取同步进度消息。
return mSyncProgress;
}
}

@ -13,36 +13,45 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.model;
// 声明代码所属的包路径。
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.RemoteException;
import android.database.Cursor;
import android.util.Log;
// 导入所需的Android类。
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
// 导入数据模型类。
import java.util.ArrayList;
// 导入Java的集合类。
public class Note {
// 定义Note类用于管理笔记数据。
private ContentValues mNoteDiffValues;
// 定义ContentValues对象用于存储笔记的差异数据。
private NoteData mNoteData;
// 定义NoteData对象用于存储笔记的文本和其他数据。
private static final String TAG = "Note";
// 定义日志标签。
/**
* Create a new note id for adding a new note to databases
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// Create a new note in the database
// 创建一个新的笔记ID并将其添加到数据库中。
ContentValues values = new ContentValues();
long createdTime = System.currentTimeMillis();
values.put(NoteColumns.CREATED_DATE, createdTime);
@ -66,41 +75,50 @@ public class Note {
}
public Note() {
// 构造函数,初始化笔记数据。
mNoteDiffValues = new ContentValues();
mNoteData = new NoteData();
}
public void setNoteValue(String key, String value) {
// 设置笔记的值。
mNoteDiffValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
public void setTextData(String key, String value) {
// 设置文本数据。
mNoteData.setTextData(key, value);
}
public void setTextDataId(long id) {
// 设置文本数据ID。
mNoteData.setTextDataId(id);
}
public long getTextDataId() {
// 获取文本数据ID。
return mNoteData.mTextDataId;
}
public void setCallDataId(long id) {
// 设置通话数据ID。
mNoteData.setCallDataId(id);
}
public void setCallData(String key, String value) {
// 设置通话数据。
mNoteData.setCallData(key, value);
}
public boolean isLocalModified() {
// 检查笔记是否被本地修改。
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
public boolean syncNote(Context context, long noteId) {
// 同步笔记到数据库。
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
@ -109,11 +127,7 @@ public class Note {
return true;
}
/**
* In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and
* {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
* note data info
*/
// 更新笔记数据
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
@ -131,17 +145,15 @@ public class Note {
}
private class NoteData {
// 内部类,用于存储笔记的文本和通话数据。
private long mTextDataId;
private ContentValues mTextDataValues;
private long mCallDataId;
private ContentValues mCallDataValues;
private static final String TAG = "NoteData";
public NoteData() {
// 构造函数,初始化数据。
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
@ -149,10 +161,12 @@ public class Note {
}
boolean isLocalModified() {
// 检查文本或通话数据是否被本地修改。
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
void setTextDataId(long id) {
// 设置文本数据ID。
if(id <= 0) {
throw new IllegalArgumentException("Text data id should larger than 0");
}
@ -160,6 +174,7 @@ public class Note {
}
void setCallDataId(long id) {
// 设置通话数据ID。
if (id <= 0) {
throw new IllegalArgumentException("Call data id should larger than 0");
}
@ -167,87 +182,22 @@ public class Note {
}
void setCallData(String key, String value) {
// 设置通话数据。
mCallDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
void setTextData(String key, String value) {
// 设置文本数据。
mTextDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* Check for safety
*/
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;
if(mTextDataValues.size() > 0) {
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
if (mTextDataId == 0) {
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mTextDataValues);
try {
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
Log.e(TAG, "Insert new text data fail with noteId" + noteId);
mTextDataValues.clear();
return null;
}
} else {
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
builder.withValues(mTextDataValues);
operationList.add(builder.build());
}
mTextDataValues.clear();
}
if(mCallDataValues.size() > 0) {
mCallDataValues.put(DataColumns.NOTE_ID, noteId);
if (mCallDataId == 0) {
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mCallDataValues);
try {
setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
Log.e(TAG, "Insert new call data fail with noteId" + noteId);
mCallDataValues.clear();
return null;
}
} else {
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mCallDataId));
builder.withValues(mCallDataValues);
operationList.add(builder.build());
}
mCallDataValues.clear();
}
if (operationList.size() > 0) {
try {
ContentProviderResult[] results = context.getContentResolver().applyBatch(
Notes.AUTHORITY, operationList);
return (results == null || results.length == 0 || results[0] == null) ? null
: ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
}
}
return null;
// 将数据推送到内容解析器。
// ...
}
}
}

@ -14,208 +14,6 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名此处在net.micode.notes.ui包下
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import java.io.IOException;
// AlarmAlertActivity类继承自Activity类同时实现了OnClickListener和OnDismissListener接口
// 意味着该类需要处理点击事件和对话框关闭事件
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
// 用于存储笔记的ID可能对应某个提醒关联的笔记
private long mNoteId;
// 用于存储笔记内容的摘要信息,长度有限制
private String mSnippet;
// 定义摘要的最大长度为60个字符
private static final int SNIPPET_PREW_MAX_LEN = 60;
// MediaPlayer对象用于播放提醒的声音
MediaPlayer mPlayer;
// 重写Activity的onCreate方法该方法在Activity创建时被调用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 请求去除Activity的标题栏使界面更简洁如果支持该特性的话
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 获取当前Activity的窗口对象
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);
}
// 获取启动该Activity的Intent对象可从中获取传递的数据等信息
Intent intent = getIntent();
try {
// 从Intent携带的数据可能是Uri格式中解析出笔记的ID具体是取路径段中的第二个元素索引为1
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 通过DataUtils工具类根据笔记ID获取笔记的摘要内容
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;
}
// 创建一个MediaPlayer实例用于后续播放提醒声音
mPlayer = new MediaPlayer();
// 通过DataUtils工具类判断该笔记在数据库中是否可见是否有效等情况
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
// 如果笔记有效,显示操作对话框,供用户进行相关操作
showActionDialog();
// 播放提醒声音
playAlarmSound();
} else {
// 如果笔记无效直接结束该Activity
finish();
}
}
// 方法用于判断屏幕是否处于开启状态
private boolean isScreenOn() {
// 获取系统的电源管理服务对象
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
// 调用其isScreenOn方法来判断屏幕状态并返回结果
return pm.isScreenOn();
}
// 方法用于播放提醒声音
private void playAlarmSound() {
// 通过RingtoneManager获取系统默认的闹钟铃声的Uri
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
// 获取系统设置中关于静音模式影响的音频流相关设置值
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
// 判断静音模式设置中是否影响闹钟音频流
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM))!= 0) {
// 如果影响设置MediaPlayer的音频流类型为对应的静音模式设置值
mPlayer.setAudioStreamType(silentModeStreams);
} else {
// 如果不影响,设置为正常的闹钟音频流类型
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置MediaPlayer的数据源为获取到的铃声Uri
mPlayer.setDataSource(this, url);
// 准备MediaPlayer使其进入可播放状态可能会涉及加载音频资源等操作
mPlayer.prepare();
// 设置为循环播放,以持续提醒
mPlayer.setLooping(true);
// 开始播放声音
mPlayer.start();
} catch (IllegalArgumentException e) {
// 如果参数设置出现异常,打印异常堆栈信息
e.printStackTrace();
} catch (SecurityException e) {
// 如果出现安全相关异常,打印异常堆栈信息
e.printStackTrace();
} catch (IllegalStateException e) {
// 如果MediaPlayer状态出现异常打印异常堆栈信息
e.printStackTrace();
} catch (IOException e) {
// 如果在读取数据源等IO操作出现异常打印异常堆栈信息
e.printStackTrace();
}
}
// 方法用于创建并显示操作对话框,给用户提供相应的操作选项
private void showActionDialog() {
// 创建一个AlertDialog的构建器对象用于构建对话框
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框的标题,这里使用了字符串资源中的应用名称
dialog.setTitle(R.string.app_name);
// 设置对话框的消息内容,显示之前获取的笔记摘要信息
dialog.setMessage(mSnippet);
// 设置对话框的确定按钮文本以及点击监听器实现了OnClickListener接口
dialog.setPositiveButton(R.string.notealert_ok, this);
// 如果屏幕处于开启状态,设置对话框的进入按钮文本以及点击监听器
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 构建并显示对话框并设置对话框关闭监听器实现了OnDismissListener接口
dialog.show().setOnDismissListener(this);
}
// 实现OnClickListener接口的方法处理对话框按钮点击事件
public void onClick(DialogInterface dialog, int which) {
switch (which) {
// 如果点击的是对话框的否定按钮(比如进入按钮等,根据具体设置而定)
case DialogInterface.BUTTON_NEGATIVE:
// 创建一个Intent用于启动NoteEditActivity可能是进入笔记编辑界面
Intent intent = new Intent(this, NoteEditActivity.class);
// 设置Intent的动作这里是查看动作
intent.setAction(Intent.ACTION_VIEW);
// 将笔记的ID通过Intent的额外数据传递给目标Activity
intent.putExtra(Intent.EXTRA_UID, mNoteId);
// 启动目标Activity
startActivity(intent);
break;
default:
break;
}
}
// 实现OnDismissListener接口的方法当对话框关闭时被调用
public void onDismiss(DialogInterface dialog) {
// 停止播放提醒声音,并释放相关资源
stopAlarmSound();
// 结束当前Activity
finish();
}
// 方法用于停止播放声音释放MediaPlayer资源
private void stopAlarmSound() {
if (mPlayer!= null) {
// 停止播放声音
mPlayer.stop();
// 释放MediaPlayer资源使其处于空闲状态
mPlayer.release();
// 将mPlayer对象置为null避免后续误操作
mPlayer = null;
}
}
}
=======
package net.micode.notes.tool;
import android.content.Context;
@ -544,4 +342,3 @@ public class BackupUtils {
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,10 +14,6 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.tool
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.tool;
import android.content.ContentProviderOperation;
@ -38,226 +34,101 @@ import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList;
import java.util.HashSet;
<<<<<<< HEAD
// DataUtils工具类提供了一系列用于操作笔记数据相关的实用方法
public class DataUtils {
// 用于日志记录的标签,方便在日志中识别该类输出的相关信息
public static final String TAG = "DataUtils";
// 批量删除笔记的方法根据给定的ContentResolver和要删除笔记的ID集合来执行删除操作
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
// 如果传入的ID集合为null记录日志并返回true可能表示无需进行删除操作的情况
=======
public class DataUtils {
public static final String TAG = "DataUtils";
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
<<<<<<< HEAD
// 如果ID集合为空记录日志并返回true表示没有实际要删除的笔记
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
return true;
}
<<<<<<< HEAD
// 创建一个用于存储内容提供器操作的列表,后续将批量执行这些操作来删除笔记
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 遍历要删除的笔记ID集合
for (long id : ids) {
// 不允许删除系统根文件夹根据特定的ID判断如果是根文件夹则记录错误日志并跳过本次删除操作
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
// 创建一个用于删除指定笔记的ContentProviderOperation构建器指定要删除的笔记的Uri
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
// 将构建好的操作添加到操作列表中
operationList.add(builder.build());
}
try {
// 通过ContentResolver批量执行操作列表中的删除操作并获取操作结果数组
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 如果结果数组为null或者长度为0或者第一个结果为null表示删除笔记失败记录日志并返回false
=======
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build());
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
<<<<<<< HEAD
// 如果成功执行删除操作返回true
return true;
} catch (RemoteException e) {
// 如果出现远程异常(比如与内容提供器通信出现问题等),记录详细的异常信息到日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 如果在应用操作过程中出现异常(比如操作不符合要求等),记录详细的异常信息到日志
=======
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
<<<<<<< HEAD
// 将指定笔记移动到指定文件夹的方法,通过更新笔记的相关字段来实现移动操作
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
// 创建一个ContentValues对象用于存储要更新的列和对应的值
ContentValues values = new ContentValues();
// 设置笔记的新父文件夹ID目标文件夹ID
values.put(NoteColumns.PARENT_ID, desFolderId);
// 设置笔记的原始父文件夹ID源文件夹ID可能用于记录移动历史等用途
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
// 设置本地修改标志,可能表示该笔记在本地有过修改操作
values.put(NoteColumns.LOCAL_MODIFIED, 1);
// 通过ContentResolver执行更新操作根据笔记的ID更新对应的记录
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
// 批量将多个笔记移动到指定文件夹的方法,与单个笔记移动类似,不过是批量操作
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
// 如果传入的笔记ID集合为null记录日志并返回true可能表示无需进行移动操作的情况
=======
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId);
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
<<<<<<< HEAD
// 创建一个用于存储内容提供器操作的列表,后续将批量执行这些操作来移动笔记
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 遍历要移动的笔记ID集合
for (long id : ids) {
// 创建一个用于更新指定笔记的ContentProviderOperation构建器指定要更新的笔记的Uri
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
// 设置笔记的新父文件夹ID目标文件夹ID
builder.withValue(NoteColumns.PARENT_ID, folderId);
// 设置本地修改标志,可能表示该笔记在本地有过修改操作
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
// 将构建好的操作添加到操作列表中
=======
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
builder.withValue(NoteColumns.PARENT_ID, folderId);
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
operationList.add(builder.build());
}
try {
<<<<<<< HEAD
// 通过ContentResolver批量执行操作列表中的更新操作并获取操作结果数组
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 如果结果数组为null或者长度为0或者第一个结果为null表示移动笔记失败记录日志并返回false
=======
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
<<<<<<< HEAD
// 如果成功执行移动操作返回true
return true;
} catch (RemoteException e) {
// 如果出现远程异常(比如与内容提供器通信出现问题等),记录详细的异常信息到日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 如果在应用操作过程中出现异常(比如操作不符合要求等),记录详细的异常信息到日志
=======
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
/**
<<<<<<< HEAD
* Notes#TYPE_SYSTEM
*/
public static int getUserFolderCount(ContentResolver resolver) {
// 通过ContentResolver查询笔记内容提供器统计符合条件的文件夹数量
// 查询的列只需要计数结果,所以使用"COUNT(*)"
// 查询条件是类型为文件夹NoteColumns.TYPE对应的是文件夹类型且父文件夹ID不是回收站文件夹ID
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
=======
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
int count = 0;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if(cursor != null) {
// 将游标移动到第一条记录(因为只有一条计数结果记录)
if(cursor.moveToFirst()) {
try {
// 获取计数结果游标中的第一列数据并赋值给count变量
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
// 如果获取数据出现越界异常,记录详细的异常信息到日志
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
// 关闭游标,释放资源
=======
if(cursor != null) {
if(cursor.moveToFirst()) {
try {
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
}
@ -265,13 +136,7 @@ public class DataUtils {
return count;
}
<<<<<<< HEAD
// 判断指定笔记在笔记数据库中是否可见(根据特定条件,比如不在回收站等)的方法
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
// 通过ContentResolver查询指定笔记的记录查询条件是类型为指定类型且父文件夹ID不是回收站文件夹ID
=======
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
@ -279,79 +144,34 @@ public class DataUtils {
null);
boolean exist = false;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 如果查询到的记录数量大于0表示该笔记存在且符合可见条件设置exist为true
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭游标,释放资源
=======
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
return exist;
}
<<<<<<< HEAD
// 判断指定笔记在笔记数据库中是否存在的方法(简单查询是否有对应记录)
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
// 通过ContentResolver查询指定笔记的记录不设置额外的查询条件
=======
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 如果查询到的记录数量大于0表示该笔记存在设置exist为true
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭游标,释放资源
=======
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
return exist;
}
<<<<<<< HEAD
// 判断指定数据(可能是某种关联数据等)在数据数据库中是否存在的方法(简单查询是否有对应记录)
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
// 通过ContentResolver查询指定数据的记录不设置额外的查询条件
=======
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 如果查询到的记录数量大于0表示该数据存在设置exist为true
}
cursor.close();
return exist;
}
// 检查指定名称的文件夹在数据库中是否可见(根据特定条件,如不在回收站且名称匹配等)的方法
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
// 通过ContentResolver查询符合条件的文件夹记录查询条件是类型为文件夹、父文件夹ID不是回收站文件夹ID且摘要可能是名称相关字段匹配指定名称
=======
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
@ -362,39 +182,22 @@ public class DataUtils {
}
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
boolean exist = false;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 如果查询到的记录数量大于0表示存在符合条件的文件夹设置exist为true
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭游标,释放资源
=======
if(cursor != null) {
if(cursor.getCount() > 0) {
exist = true;
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
return exist;
}
<<<<<<< HEAD
// 获取指定文件夹下所有笔记对应的小部件相关属性集合的方法(可能用于展示小部件相关信息等)
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
// 通过ContentResolver查询指定文件夹下笔记的小部件ID和小部件类型信息
=======
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
@ -402,94 +205,45 @@ public class DataUtils {
null);
HashSet<AppWidgetAttribute> set = null;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (c!= null) {
// 将游标移动到第一条记录
=======
if (c != null) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (c.moveToFirst()) {
set = new HashSet<AppWidgetAttribute>();
do {
try {
<<<<<<< HEAD
// 创建一个AppWidgetAttribute对象用于存储小部件相关属性
AppWidgetAttribute widget = new AppWidgetAttribute();
// 从游标中获取小部件ID并赋值给widget对象
widget.widgetId = c.getInt(0);
// 从游标中获取小部件类型并赋值给widget对象
widget.widgetType = c.getInt(1);
// 将widget对象添加到集合中
set.add(widget);
} catch (IndexOutOfBoundsException e) {
// 如果获取数据出现越界异常,记录详细的异常信息到日志
=======
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1);
set.add(widget);
} catch (IndexOutOfBoundsException e) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Log.e(TAG, e.toString());
}
} while (c.moveToNext());
}
<<<<<<< HEAD
// 关闭游标,释放资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
c.close();
}
return set;
}
<<<<<<< HEAD
// 根据笔记ID获取对应的电话号码的方法可能是与笔记关联的电话号码
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
// 通过ContentResolver查询指定笔记关联的数据表可能是通话记录相关的数据表获取电话号码列的值
// 查询条件是笔记ID匹配且MIME类型匹配可能用于确定数据类型
=======
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },
null);
<<<<<<< HEAD
if (cursor!= null && cursor.moveToFirst()) {
try {
// 如果查询到结果且游标移动到第一条记录,获取电话号码字符串并返回
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
// 如果获取数据出现越界异常,记录详细的异常信息到日志
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
// 关闭游标,释放资源
=======
if (cursor != null && cursor.moveToFirst()) {
try {
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
}
return "";
}
<<<<<<< HEAD
// 根据电话号码和通话日期获取对应的笔记ID的方法可能用于反向查找关联笔记
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
// 通过ContentResolver查询指定数据表可能是通话记录相关的数据表获取笔记ID列的值
// 查询条件是通话日期匹配、MIME类型匹配且电话号码匹配通过自定义函数PHONE_NUMBERS_EQUAL判断
=======
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
@ -497,19 +251,6 @@ public class DataUtils {
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null);
<<<<<<< HEAD
if (cursor!= null) {
if (cursor.moveToFirst()) {
try {
// 如果查询到结果且游标移动到第一条记录获取笔记ID并返回
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
// 如果获取数据出现越界异常,记录详细的异常信息到日志
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
// 关闭游标,释放资源
=======
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
@ -518,15 +259,11 @@ public class DataUtils {
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
return 0;
}
<<<<<<< HEAD
// 根据笔记ID获取对应的摘要信息的方法
=======
public static String getSnippetById(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
@ -556,4 +293,3 @@ public class DataUtils {
return snippet;
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -13,291 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.tool
package net.micode.notes.tool;
// 声明代码所属的包路径。
// GTaskStringUtils类从名称推测可能是用于处理与GTask相关的字符串操作的工具类
// 目前类中主要定义了一系列的字符串常量,方便在整个项目中统一使用这些特定的字符串标识
public class GTaskStringUtils {
// 定义GTaskStringUtils类用于存储GTask相关的字符串常量。
// 表示GTask JSON数据中"action_id"字段对应的字符串常量可能用于在解析或构建GTask相关JSON数据时
// 作为键来获取或设置对应的值,代表某个操作的唯一标识符
// 定义GTask JSON相关常量。
public final static String GTASK_JSON_ACTION_ID = "action_id";
// 表示GTask JSON数据中"action_list"字段对应的字符串常量,可能用于表示操作相关的列表信息,
// 比如涉及到的任务列表等情况
public final static String GTASK_JSON_ACTION_LIST = "action_list";
// 表示GTask JSON数据中"action_type"字段对应的字符串常量,用于表示操作的类型,
// 后续有定义具体的操作类型值(如创建、获取全部等)
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
// 表示GTask JSON数据中"action_type"字段具体取值为"create"时对应的字符串常量,
// 意味着对应的操作是创建类型的操作,比如创建任务、创建列表等
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
// 表示GTask JSON数据中"action_type"字段具体取值为"get_all"时对应的字符串常量,
// 意味着对应的操作是获取全部相关内容的操作,比如获取全部任务、获取全部列表等
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
// 表示GTask JSON数据中"action_type"字段具体取值为"move"时对应的字符串常量,
// 意味着对应的操作是移动相关内容的操作,比如移动任务到其他列表等情况
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
// 表示GTask JSON数据中"action_type"字段具体取值为"update"时对应的字符串常量,
// 意味着对应的操作是更新相关内容的操作,比如更新任务的属性等情况
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
// 表示GTask JSON数据中"creator_id"字段对应的字符串常量,可能用于标识执行某个操作或者创建某个对象的用户的唯一标识符
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
// 表示GTask JSON数据中"child_entity"字段对应的字符串常量,可能用于表示某个实体(如任务、列表等)的子实体相关信息
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
// 表示GTask JSON数据中"client_version"字段对应的字符串常量,可能用于记录客户端的版本信息,
// 比如在与服务器交互等场景下,标识客户端的版本情况
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
// 表示GTask JSON数据中"completed"字段对应的字符串常量,可能用于表示某个任务是否已完成的状态信息,
// 比如布尔值对应的字符串表示("true"或"false"等情况)
public final static String GTASK_JSON_COMPLETED = "completed";
// 表示GTask JSON数据中"current_list_id"字段对应的字符串常量,可能用于标识当前所在的任务列表的唯一标识符,
// 比如当前操作关联的是哪个具体的任务列表
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
// 表示GTask JSON数据中"default_list_id"字段对应的字符串常量,可能用于标识默认任务列表的唯一标识符,
// 例如新创建任务默认归属的列表等情况
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
// 表示GTask JSON数据中"deleted"字段对应的字符串常量,可能用于表示某个对象(如任务、列表等)是否已被删除的状态信息
public final static String GTASK_JSON_DELETED = "deleted";
// 表示GTask JSON数据中"dest_list"字段对应的字符串常量,可能用于在移动等操作中,表示目标任务列表相关信息,
// 比如移动任务到的目标列表等情况
public final static String GTASK_JSON_DEST_LIST = "dest_list";
// 表示GTask JSON数据中"dest_parent"字段对应的字符串常量,可能用于在移动或关联等操作中,
// 表示目标父对象(如任务的父任务、列表的父列表等)相关信息
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
// 表示GTask JSON数据中"dest_parent_type"字段对应的字符串常量,可能用于明确目标父对象的类型,
// 比如是任务类型还是列表类型等情况
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
// 表示GTask JSON数据中"entity_delta"字段对应的字符串常量,可能用于表示实体(如任务、列表等)的变化量相关信息,
// 比如属性的变化差值等情况,常用于更新操作的相关逻辑中
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
// 表示GTask JSON数据中"entity_type"字段对应的字符串常量,可能用于明确实体的类型,
// 比如是任务、列表还是其他自定义类型等情况
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
// 表示GTask JSON数据中"get_deleted"字段对应的字符串常量,可能用于获取已删除对象相关信息的操作标识,
// 比如请求获取已删除的任务、列表等情况
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
// 表示GTask JSON数据中"id"字段对应的字符串常量,通常用于表示各种对象(任务、列表、用户等)的唯一标识符
public final static String GTASK_JSON_ID = "id";
// 表示GTask JSON数据中"index"字段对应的字符串常量,可能用于表示某个对象在列表中的顺序索引等情况,
// 比如任务在任务列表中的排列顺序等
public final static String GTASK_JSON_INDEX = "index";
// 表示GTask JSON数据中"last_modified"字段对应的字符串常量,可能用于记录对象最后一次被修改的时间戳等信息,
// 方便进行数据同步、版本控制等操作
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
// 表示GTask JSON数据中"latest_sync_point"字段对应的字符串常量,可能用于标识最近一次数据同步的关键点信息,
// 比如同步的时间、版本号等情况,用于确保数据在不同端的一致性
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
// 表示GTask JSON数据中"list_id"字段对应的字符串常量,通常用于表示任务列表的唯一标识符,
// 在很多涉及任务与列表关联的操作中会用到
public final static String GTASK_JSON_LIST_ID = "list_id";
// 表示GTask JSON数据中"lists"字段对应的字符串常量,可能用于表示一组任务列表相关的数据结构,
// 比如包含多个任务列表的集合等情况
public final static String GTASK_JSON_LISTS = "lists";
// 表示GTask JSON数据中"name"字段对应的字符串常量,通常用于表示对象(任务、列表、用户等)的名称信息,
// 方便展示和区分不同的对象
public final static String GTASK_JSON_NAME = "name";
// 表示GTask JSON数据中"new_id"字段对应的字符串常量,可能用于在某些操作(如复制、移动后重新生成标识符等情况)下,
// 表示新生成的对象的唯一标识符
public final static String GTASK_JSON_NEW_ID = "new_id";
// 表示GTask JSON数据中"notes"字段对应的字符串常量,可能用于表示与任务、列表等相关的备注信息,
// 比如对任务的详细描述、对列表用途的说明等情况
public final static String GTASK_JSON_NOTES = "notes";
// 表示GTask JSON数据中"parent_id"字段对应的字符串常量,通常用于表示某个对象(如任务、列表等)的父对象的唯一标识符,
// 用于构建对象之间的层级关系等情况
public final static String GTASK_JSON_PARENT_ID = "parent_id";
// 表示GTask JSON数据中"prior_sibling_id"字段对应的字符串常量,可能用于表示在同一层级中,
// 当前对象之前的兄弟对象的唯一标识符,比如在任务列表中某个任务之前的相邻任务的标识等情况
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
// 表示GTask JSON数据中"results"字段对应的字符串常量,可能用于存储操作后的结果信息,
// 比如执行某个任务操作后返回的结果集等情况
public final static String GTASK_JSON_RESULTS = "results";
// 表示GTask JSON数据中"source_list"字段对应的字符串常量,可能用于在移动、复制等操作中,
// 表示源任务列表相关信息,即操作对象原来所在的列表等情况
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
// 表示GTask JSON数据中"tasks"字段对应的字符串常量,通常用于表示一组任务相关的数据结构,
// 比如包含多个任务的集合等情况
public final static String GTASK_JSON_TASKS = "tasks";
// 表示GTask JSON数据中"type"字段对应的字符串常量,用于表示对象的类型,
// 后续有定义具体的类型值(如任务、组等情况)
public final static String GTASK_JSON_TYPE = "type";
// 表示GTask JSON数据中"type"字段具体取值为"GROUP"时对应的字符串常量,意味着对应的对象类型是组,
// 比如任务分组等情况,用于区分不同类型的对象
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
// 表示GTask JSON数据中"type"字段具体取值为"TASK"时对应的字符串常量,意味着对应的对象类型是任务,
// 是最常见的操作对象类型之一
public final static String GTASK_JSON_TYPE_TASK = "TASK";
// 表示GTask JSON数据中"user"字段对应的字符串常量,可能用于表示与用户相关的信息,
// 比如操作的执行者、任务的创建者等用户相关情况
public final static String GTASK_JSON_USER = "user";
// 表示特定的文件夹前缀字符串常量可能用于标识是MIUI笔记相关的文件夹
// 在文件系统或者数据存储中方便区分不同来源、用途的文件夹
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]";
// 表示默认文件夹名称的字符串常量,可能用于在没有指定具体文件夹时,
// 作为默认的文件夹名称使用,比如新创建的笔记、任务等默认归属的文件夹
public final static String FOLDER_DEFAULT = "Default";
// 表示通话笔记文件夹名称的字符串常量,可能专门用于存放与通话记录相关的笔记内容,
// 方便对这类特定类型的笔记进行分类管理
public final static String FOLDER_CALL_NOTE = "Call_Note";
// 表示元数据文件夹名称的字符串常量,可能用于存放各种元数据相关的文件或信息,
// 比如数据的描述信息、配置信息等情况
public final static String FOLDER_META = "METADATA";
// 表示元数据中GTask ID头部信息的字符串常量可能用于在元数据结构中
// 标识GTask相关的唯一标识符所在的字段等情况
public final static String META_HEAD_GTASK_ID = "meta_gid";
// 表示元数据中笔记相关头部信息的字符串常量,可能用于在元数据结构中,
// 标识笔记相关内容所在的字段等情况
public final static String META_HEAD_NOTE = "meta_note";
// 表示元数据中数据相关头部信息的字符串常量,可能用于在元数据结构中,
// 标识其他通用数据相关内容所在的字段等情况
public final static String META_HEAD_DATA = "meta_data";
// 表示元数据中笔记名称相关的特定字符串常量,通常用于给出一个提示信息,
// 告知不要对这类特殊的元数据笔记进行更新和删除操作等情况
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE";
}
=======
package net.micode.notes.tool;
public class GTaskStringUtils {
public final static String GTASK_JSON_ACTION_ID = "action_id";
public final static String GTASK_JSON_ACTION_LIST = "action_list";
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
// ...
// 定义GTask操作类型常量。
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
// ...
// 定义GTask其他相关常量。
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
// ...
public final static String GTASK_JSON_COMPLETED = "completed";
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
public final static String GTASK_JSON_DELETED = "deleted";
public final static String GTASK_JSON_DEST_LIST = "dest_list";
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
public final static String GTASK_JSON_ID = "id";
public final static String GTASK_JSON_INDEX = "index";
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
public final static String GTASK_JSON_LIST_ID = "list_id";
public final static String GTASK_JSON_LISTS = "lists";
public final static String GTASK_JSON_NAME = "name";
public final static String GTASK_JSON_NEW_ID = "new_id";
public final static String GTASK_JSON_NOTES = "notes";
public final static String GTASK_JSON_PARENT_ID = "parent_id";
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
public final static String GTASK_JSON_RESULTS = "results";
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
public final static String GTASK_JSON_TASKS = "tasks";
public final static String GTASK_JSON_TYPE = "type";
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
public final static String GTASK_JSON_TYPE_TASK = "TASK";
public final static String GTASK_JSON_USER = "user";
// 定义MIUI文件夹前缀和特殊文件夹名称。
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]";
public final static String FOLDER_DEFAULT = "Default";
public final static String FOLDER_CALL_NOTE = "Call_Note";
public final static String FOLDER_META = "METADATA";
// ...
// 定义元数据相关常量。
public final static String META_HEAD_GTASK_ID = "meta_gid";
public final static String META_HEAD_NOTE = "meta_note";
public final static String META_HEAD_DATA = "meta_data";
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE";
// ...
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,10 +14,6 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.tool
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.tool;
import android.content.Context;
@ -26,32 +22,24 @@ import android.preference.PreferenceManager;
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
<<<<<<< HEAD
// ResourceParser类从名称来看是一个资源解析相关的工具类用于获取和处理各种与界面显示相关的资源如颜色、背景图片、字体样式等资源的相关操作
public class ResourceParser {
// 定义颜色相关的常量,用整数来表示不同的颜色选项,这里分别对应黄色、蓝色、白色、绿色、红色,方便后续根据这些常量来引用对应的颜色资源或进行相关逻辑判断
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
// 定义默认的背景颜色常量其值为YELLOW即黄色表示在没有特殊设置的情况下默认使用的背景颜色对应的标识
public static final int BG_DEFAULT_COLOR = YELLOW;
// 定义字体大小相关的常量,用整数来区分不同的字体大小级别,这里分别对应小、中、大、超大字体,方便后续在设置字体相关逻辑中使用这些常量进行判断和操作
public static final int TEXT_SMALL = 0;
public static final int TEXT_MEDIUM = 1;
public static final int TEXT_LARGE = 2;
public static final int TEXT_SUPER = 3;
// 定义默认的字体大小常量其值为TEXT_MEDIUM即中等字体大小表示在没有特殊设置的情况下默认使用的字体大小对应的标识
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
// 内部静态类NoteBgResources用于管理笔记编辑相关的背景资源如背景图片资源等将相关资源的获取逻辑封装在内部类中使代码结构更清晰
public static class NoteBgResources {
// 定义一个私有的静态整数数组用于存储笔记编辑页面背景的资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的编辑页面背景图片资源
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
@ -60,7 +48,6 @@ public class ResourceParser {
R.drawable.edit_red
};
// 定义一个私有的静态整数数组用于存储笔记编辑页面标题背景的资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的编辑页面标题背景图片资源
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
@ -69,118 +56,24 @@ public class ResourceParser {
R.drawable.edit_title_red
};
// 根据传入的颜色标识ID对应前面定义的颜色常量获取笔记编辑页面的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
public class ResourceParser {
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
public static final int BG_DEFAULT_COLOR = YELLOW;
public static final int TEXT_SMALL = 0;
public static final int TEXT_MEDIUM = 1;
public static final int TEXT_LARGE = 2;
public static final int TEXT_SUPER = 3;
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
public static class NoteBgResources {
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
};
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
<<<<<<< HEAD
// 根据传入的颜色标识ID对应前面定义的颜色常量获取笔记编辑页面标题的背景图片资源ID方便在代码中设置对应的标题背景图片资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
<<<<<<< HEAD
// 根据给定的上下文Context获取默认的背景颜色标识ID判断逻辑基于用户在偏好设置Preference中是否设置了特定的背景颜色相关选项
public static int getDefaultBgId(Context context) {
// 通过PreferenceManager获取默认的共享偏好设置对象然后检查是否设置了特定的背景颜色设置键对应的布尔值可能表示用户是否手动设置过背景颜色
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
// 如果用户设置过随机生成一个在背景资源数组长度范围内的索引值对应不同颜色的背景资源作为返回的背景颜色标识ID实现随机选择背景颜色的效果
return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length);
} else {
// 如果用户没有设置过返回默认的背景颜色标识ID即前面定义的BG_DEFAULT_COLOR
=======
public static int getDefaultBgId(Context context) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length);
} else {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
return BG_DEFAULT_COLOR;
}
}
<<<<<<< HEAD
// 内部静态类NoteItemBgResources用于管理笔记列表项相关的背景资源如不同位置的列表项背景图片资源等同样将相关资源的获取逻辑封装在内部类中便于代码的组织和维护
public static class NoteItemBgResources {
// 定义一个私有的静态整数数组用于存储列表项第一个元素比如列表中第一个笔记项的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
private final static int[] BG_FIRST_RESOURCES = new int[]{
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
};
// 定义一个私有的静态整数数组用于存储列表项中间元素非第一个和最后一个笔记项的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
private final static int[] BG_NORMAL_RESOURCES = new int[]{
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
};
// 定义一个私有的静态整数数组用于存储列表项最后一个元素比如列表中最后一个笔记项的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
private final static int[] BG_LAST_RESOURCES = new int[]{
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down,
};
// 定义一个私有的静态整数数组用于存储列表项单独存在比如列表中只有一个笔记项时的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
private final static int[] BG_SINGLE_RESOURCES = new int[]{
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
};
// 根据传入的颜色标识ID对应前面定义的颜色常量获取列表项第一个元素的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
public static class NoteItemBgResources {
private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up,
@ -214,48 +107,28 @@ public class ResourceParser {
R.drawable.list_red_single
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
<<<<<<< HEAD
// 根据传入的颜色标识ID对应前面定义的颜色常量获取列表项最后一个元素的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
<<<<<<< HEAD
// 根据传入的颜色标识ID对应前面定义的颜色常量获取列表项单独存在时的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
<<<<<<< HEAD
// 根据传入的颜色标识ID对应前面定义的颜色常量获取列表项中间元素的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
<<<<<<< HEAD
// 获取文件夹的背景图片资源ID这里固定返回一个表示文件夹背景图片的资源ID可能所有文件夹使用相同的背景图片样式
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
<<<<<<< HEAD
// 内部静态类WidgetBgResources用于管理小部件相关的背景资源如不同尺寸小部件的背景图片资源等将相关资源的获取逻辑封装在内部类中以更好地组织代码结构
public static class WidgetBgResources {
// 定义一个私有的静态整数数组用于存储2倍尺寸小部件的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
@ -264,34 +137,10 @@ public class ResourceParser {
R.drawable.widget_2x_red,
};
// 根据传入的颜色标识ID对应前面定义的颜色常量获取2倍尺寸小部件的背景图片资源ID方便在代码中设置对应的小部件背景图片资源
=======
public static class WidgetBgResources {
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red,
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
<<<<<<< HEAD
// 定义一个私有的静态整数数组用于存储4倍尺寸小部件的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
private final static int[] BG_4X_RESOURCES = new int[]{
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
};
// 根据传入的颜色标识ID对应前面定义的颜色常量获取4倍尺寸小部件的背景图片资源ID方便在代码中设置对应的小部件背景图片资源
=======
private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
@ -300,25 +149,11 @@ public class ResourceParser {
R.drawable.widget_4x_red
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}
<<<<<<< HEAD
// 内部静态类TextAppearanceResources用于管理文本外观相关的资源如不同字体大小对应的样式资源等将相关资源的获取逻辑封装在内部类中使代码结构更清晰易读
public static class TextAppearanceResources {
// 定义一个私有的静态整数数组用于存储不同字体大小对应的文本外观样式资源ID每个元素对应一种字体大小级别顺序与前面定义的字体大小常量对应的对应样式资源
private final static int[] TEXTAPPEARANCE_RESOURCES = new int[]{
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
};
// 根据传入的字体大小标识ID对应前面定义的字体大小常量获取对应的文本外观样式资源ID同时处理了ID超出资源数组长度的情况可能是由于数据异常等原因此时返回默认的字体大小对应的资源ID
=======
public static class TextAppearanceResources {
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
@ -327,7 +162,6 @@ public class ResourceParser {
R.style.TextAppearanceSuper
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getTexAppearanceResource(int id) {
/**
* HACKME: Fix bug of store the resource id in shared preference.
@ -340,16 +174,8 @@ public class ResourceParser {
return TEXTAPPEARANCE_RESOURCES[id];
}
<<<<<<< HEAD
// 获取文本外观样式资源数组的长度可用于判断传入的字体大小标识ID是否越界等情况也便于在其他相关逻辑中了解资源的数量情况
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
<<<<<<< HEAD
}
=======
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -5,7 +5,7 @@
* 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
* 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,
@ -14,10 +14,7 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类属于net.micode.notes.ui包通常用于存放与用户界面相关的类
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
// 包声明表明该类所在的包名这里是笔记应用相关的UI包
package net.micode.notes.ui;
import android.app.Activity;
@ -43,251 +40,135 @@ import net.micode.notes.tool.DataUtils;
import java.io.IOException;
<<<<<<< HEAD
// AlarmAlertActivity类继承自Activity类意味着它是一个安卓中的Activity用于展示用户界面。
// 同时实现了OnClickListener和OnDismissListener接口用于处理对话框的点击和关闭事件
// AlarmAlertActivity类继承自Activity用于处理闹钟提醒相关的界面展示和操作逻辑同时实现了OnClickListener和OnDismissListener接口来处理对话框的点击和关闭事件
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
// 用于存储与提醒相关联的笔记的ID可能后续用于查询、操作该笔记的相关信息
// 记录笔记的ID用于后续查找对应的笔记等相关操作
private long mNoteId;
// 用于存储笔记的摘要信息,将展示在提醒界面等地方给用户查看
// 笔记内容的摘要信息,用于在提醒对话框中展示
private String mSnippet;
// 定义摘要信息的最大长度为60个字符超过这个长度会进行截断处理
// 摘要信息最大长度限制,超过这个长度会进行截断处理并添加额外提示信息
private static final int SNIPPET_PREW_MAX_LEN = 60;
// MediaPlayer对象,用于播放提醒的声音,比如闹钟铃声等
// 用于播放闹钟提醒声音的MediaPlayer对象
MediaPlayer mPlayer;
// 重写Activity的onCreate方法该方法在Activity被创建时调用,用于进行初始化等相关操作
// Activity创建时调用的方法,进行一些初始化操作
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 请求去除Activity的标题栏使界面更加简洁仅在支持该特性的安卓版本和设备上生效
// 设置窗口特性,去除标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 获取当前Activity的窗口对象后续用于设置窗口的各种属性
final Window win = getWindow();
// 设置窗口属性,使窗口在屏幕锁定时也能够显示出来,方便用户看到提醒信息
// 设置窗口属性,使得在屏幕锁定时也能显示该Activity
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
// 调用isScreenOn方法判断屏幕是否处于开启状态如果屏幕未开启
// 判断屏幕是否已经点亮,如果未点亮则添加更多保持屏幕亮起等相关的窗口属性
if (!isScreenOn()) {
// 给窗口添加一系列标志位,用于确保屏幕在必要时亮起、保持常亮,并且允许在屏幕锁定时进行相关操作,
// 同时设置布局相关的装饰属性,以保证界面能正确显示
=======
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId;
private String mSnippet;
private static final int SNIPPET_PREW_MAX_LEN = 60;
MediaPlayer mPlayer;
@Override
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()) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
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);
}
<<<<<<< HEAD
// 获取启动该Activity的Intent对象Intent中可能携带了与提醒相关的数据比如笔记的相关信息等
Intent intent = getIntent();
try {
// 从Intent携带的数据中解析出笔记的ID具体是从Intent的数据Uri的路径段中获取第二个元素索引为1并转换为长整型
// 从传入的Intent中获取数据部分解析出笔记的ID这里假设传入的数据格式是特定的路径形式从中提取第二个路径段作为笔记ID
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 通过DataUtils工具类的方法根据笔记ID从内容提供器中获取笔记的摘要信息
// 通过工具类DataUtils根据笔记ID获取笔记的摘要内容
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
// 如果获取到的摘要信息长度大于设定的最大长度SNIPPET_PREW_MAX_LEN
// 如果摘要内容长度超过最大限制长度,进行截断处理,并添加相应的提示字符串(比如表示内容还有更多等提示)
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
// 则进行截断处理截取前60个字符并添加一个特定的提示字符串从资源文件中获取否则保持原摘要信息不变
} catch (IllegalArgumentException e) {
// 如果在解析笔记ID或者获取摘要信息过程中出现参数异常比如格式不正确等原因打印异常堆栈信息并直接返回不再执行后续操作
=======
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) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
e.printStackTrace();
return;
}
<<<<<<< HEAD
// 创建一个MediaPlayer实例用于后续播放提醒声音
// 创建MediaPlayer对象用于播放声音
mPlayer = new MediaPlayer();
// 通过DataUtils工具类判断该笔记在笔记数据库中是否可见可能涉及是否被删除、是否处于有效状态等条件判断
// 传入笔记ID以及笔记类型这里是普通笔记类型Notes.TYPE_NOTE进行判断
// 判断笔记是否在笔记数据库中可见通过DataUtils工具类的方法进行判断如果可见则展示操作对话框并播放闹钟声音否则直接结束该Activity
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
// 如果笔记可见即满足有效条件调用showActionDialog方法显示操作对话框供用户进行相应操作
showActionDialog();
// 调用playAlarmSound方法播放提醒声音
playAlarmSound();
} else {
// 如果笔记不可见直接结束当前Activity意味着不需要展示提醒相关的界面了
=======
mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog();
playAlarmSound();
} else {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
finish();
}
}
<<<<<<< HEAD
// 私有方法,用于判断屏幕是否处于开启状态
// 判断屏幕是否处于开启状态的方法通过获取PowerManager服务并调用其isScreenOn方法来判断
private boolean isScreenOn() {
// 获取系统的电源管理服务对象,用于查询屏幕的电源状态等信息
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
// 调用电源管理服务对象的isScreenOn方法返回屏幕是否开启的布尔值结果
return pm.isScreenOn();
}
// 私有方法,用于播放提醒声音,比如播放闹钟铃声等
// 播放闹钟提醒声音的方法
private void playAlarmSound() {
// 通过RingtoneManager获取系统默认的闹钟铃声的Uri用于指定MediaPlayer要播放的音频资源
// 获取系统默认的闹钟铃声的Uri
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
// 从系统设置中获取静音模式影响的音频流相关设置值,用于判断在静音模式下闹钟声音的播放情况
// 获取系统设置中受静音模式影响的音频流相关设置
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
// 判断静音模式设置中是否影响闹钟音频流如果影响对应位的值不为0
// 根据静音模式相关设置来决定设置MediaPlayer的音频流类型如果闹钟音频流在受影响的范围内则按该设置否则设置为常规的闹钟音频流类型
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM))!= 0) {
// 设置MediaPlayer的音频流类型为获取到的静音模式设置值按照系统静音模式的相关配置来播放声音
mPlayer.setAudioStreamType(silentModeStreams);
} else {
// 如果静音模式不影响闹钟音频流,设置为正常的闹钟音频流类型,确保闹钟声音正常播放
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置MediaPlayer的数据源为获取到的铃声Uri,指定要播放的音频文件等资源
// 设置MediaPlayer要播放的数据源为获取到的铃声Uri
mPlayer.setDataSource(this, url);
// 准备MediaPlayer使其进入可播放状态,这个过程可能涉及加载音频资源、初始化播放相关参数等操作
// 准备MediaPlayer使其处于可播放状态(可能会涉及加载音频资源等操作)
mPlayer.prepare();
// 设置MediaPlayer为循环播放模式,使提醒声音能够持续播放,直到用户手动停止或者其他相关逻辑结束播放
// 设置为循环播放
mPlayer.setLooping(true);
// 启动MediaPlayer开始播放提醒声音
// 开始播放声音
mPlayer.start();
} catch (IllegalArgumentException e) {
// 如果在设置数据源等过程中出现参数异常(比如参数不合法等原因),打印异常堆栈信息
// 捕获非法参数异常并打印堆栈信息,通常是参数设置不符合要求等情况导致
e.printStackTrace();
} catch (SecurityException e) {
// 如果出现安全相关异常(比如没有权限访问音频资源等原因),打印异常堆栈信息
// 捕获安全相关异常并打印堆栈信息,比如可能缺少相应权限等情况导致
e.printStackTrace();
} catch (IllegalStateException e) {
// 如果MediaPlayer的状态出现异常比如在不恰当的状态下执行了某些操作等原因打印异常堆栈信息
// 捕获非法状态异常并打印堆栈信息,比如在不恰当的状态下调用方法等情况导致
e.printStackTrace();
} catch (IOException e) {
// 如果在读取数据源等IO操作过程中出现异常比如音频文件损坏、无法读取等原因打印异常堆栈信息
=======
private boolean isScreenOn() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
}
private void playAlarmSound() {
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
mPlayer.setAudioStreamType(silentModeStreams);
} else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
mPlayer.setDataSource(this, url);
mPlayer.prepare();
mPlayer.setLooping(true);
mPlayer.start();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
// 捕获输入输出相关异常并打印堆栈信息,比如读取音频数据源出错等情况导致
e.printStackTrace();
}
}
<<<<<<< HEAD
// 私有方法,用于创建并显示操作对话框,给用户提供相应的操作选项,比如确认、进入编辑等操作
private void showActionDialog() {
// 创建一个AlertDialog的构建器对象用于方便地构建AlertDialog对话框
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框的标题这里使用了字符串资源中的应用名称R.string.app_name对应的字符串作为标题
dialog.setTitle(R.string.app_name);
// 设置对话框的消息内容,展示之前获取到的笔记摘要信息,让用户了解是哪个笔记的提醒
dialog.setMessage(mSnippet);
// 设置对话框的确定按钮通常是一个积极的操作按钮文本以及点击监听器点击监听器使用当前类实现的OnClickListener接口
// 意味着点击该按钮会触发当前类中对应的onClick方法
dialog.setPositiveButton(R.string.notealert_ok, this);
// 如果屏幕处于开启状态设置对话框的另一个按钮可能是进入编辑等操作按钮文本以及点击监听器同样使用当前类实现的OnClickListener接口
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 构建并显示对话框同时设置对话框的关闭监听器使用当前类实现的OnDismissListener接口
// 意味着对话框关闭时会触发当前类中对应的onDismiss方法
dialog.show().setOnDismissListener(this);
}
// 实现OnClickListener接口的方法用于处理对话框按钮的点击事件根据点击的按钮不同执行相应的逻辑
public void onClick(DialogInterface dialog, int which) {
switch (which) {
// 如果点击的是对话框中的否定按钮(具体含义根据设置的文本而定,这里可能是进入编辑按钮等情况)
case DialogInterface.BUTTON_NEGATIVE:
// 创建一个Intent用于启动NoteEditActivity可能是进入对应的笔记编辑界面方便用户对笔记进行编辑等操作
Intent intent = new Intent(this, NoteEditActivity.class);
// 设置Intent的动作这里设置为查看ACTION_VIEW动作表明是查看相关的操作意图
intent.setAction(Intent.ACTION_VIEW);
// 通过Intent的额外数据传递机制将笔记的ID传递给目标ActivityNoteEditActivity以便目标Activity能获取并处理对应的笔记
intent.putExtra(Intent.EXTRA_UID, mNoteId);
// 启动目标Activity跳转到对应的笔记编辑界面
=======
// 展示操作对话框的方法,对话框中包含笔记摘要信息等内容以及相应的操作按钮
private void showActionDialog() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框标题为应用名称(通过资源文件中的字符串资源获取)
dialog.setTitle(R.string.app_name);
// 设置对话框消息内容为笔记摘要信息
dialog.setMessage(mSnippet);
// 设置对话框的确认按钮(通常表示知晓、确认等操作)以及对应的点击事件监听器(当前类实现了该监听器接口)
dialog.setPositiveButton(R.string.notealert_ok, this);
// 如果屏幕处于开启状态,设置对话框的进入按钮(可能用于进入笔记编辑等相关操作)以及对应的点击事件监听器
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 构建并显示对话框,并设置对话框关闭的监听器(当前类也实现了该监听器接口)
dialog.show().setOnDismissListener(this);
}
// 实现OnClickListener接口的方法用于处理对话框按钮点击事件
public void onClick(DialogInterface dialog, int which) {
switch (which) {
// 如果点击的是对话框的否定按钮(比如进入按钮)
case DialogInterface.BUTTON_NEGATIVE:
// 创建一个Intent用于启动笔记编辑ActivityNoteEditActivity设置查看操作的Action并将笔记ID作为额外数据传递过去
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
startActivity(intent);
break;
default:
@ -295,33 +176,15 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
<<<<<<< HEAD
// 实现OnDismissListener接口的方法当对话框关闭时会被调用用于执行对话框关闭后的相关操作
public void onDismiss(DialogInterface dialog) {
// 调用stopAlarmSound方法停止播放提醒声音并释放相关的资源如MediaPlayer占用的内存等
stopAlarmSound();
// 结束当前Activity关闭提醒相关的界面完成整个提醒流程
finish();
}
// 私有方法用于停止播放提醒声音并释放MediaPlayer资源将其置为null避免后续出现问题
private void stopAlarmSound() {
if (mPlayer!= null) {
// 停止MediaPlayer的播放如果正在播放声音则立即停止
mPlayer.stop();
// 释放MediaPlayer占用的资源使其回到初始未使用状态
mPlayer.release();
// 将mPlayer对象置为null防止后续误操作该对象同时也符合资源释放后的正确使用习惯
mPlayer = null;
}
}
}
=======
// 实现OnDismissListener接口的方法用于处理对话框关闭事件
public void onDismiss(DialogInterface dialog) {
// 调用停止闹钟声音的方法关闭并释放MediaPlayer资源
stopAlarmSound();
// 结束当前Activity
finish();
}
// 停止闹钟声音的方法释放MediaPlayer相关资源
private void stopAlarmSound() {
if (mPlayer!= null) {
mPlayer.stop();
@ -330,4 +193,3 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
<<<<<<< HEAD
// 包声明表明该类属于net.micode.notes.ui包通常与应用的用户界面相关模块放在一起不过从类名来看这个类更偏向于处理提醒相关的逻辑属于UI层与业务逻辑交互的部分
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.ui;
// 声明代码所属的包。
import android.app.AlarmManager;
import android.app.PendingIntent;
@ -30,104 +28,55 @@ import android.database.Cursor;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
// 导入所需的Android类和接口以及应用程序特定的类。
<<<<<<< HEAD
// AlarmInitReceiver类继承自BroadcastReceiver用于接收系统广播消息在这里可能是用于处理与提醒初始化相关的广播事件
public class AlarmInitReceiver extends BroadcastReceiver {
// 定义一个继承自BroadcastReceiver的类用于处理闹钟初始化。
// 定义一个字符串数组用于指定查询数据库时要获取的列这里只获取笔记的ID和提醒日期两列信息方便后续操作使用
private static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
// 定义一个数组,指定查询数据库时需要返回的列。
// 定义一个常量表示查询结果中笔记ID列对应的索引位置方便后续从游标Cursor中获取对应的数据这里索引为0因为在PROJECTION数组中它排在第一位
private static final int COLUMN_ID = 0;
// 定义一个常量表示查询结果中提醒日期列对应的索引位置同样方便从游标中获取对应的数据这里索引为1因为在PROJECTION数组中它排在第二位
private static final int COLUMN_ALERTED_DATE = 1;
// 定义列索引常量,用于在查询结果中快速定位到特定的列。
// 重写BroadcastReceiver的onReceive方法该方法会在接收到匹配的广播消息时被调用是处理广播逻辑的核心方法
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前系统时间的毫秒数,用于后续与笔记的提醒日期进行比较,判断哪些笔记的提醒时间已经到了或者即将到了
// 实现onReceive方法这是BroadcastReceiver的核心方法当接收到广播时会被调用。
long currentDate = System.currentTimeMillis();
// 获取当前时间的毫秒值。
// 通过ContentResolver查询笔记内容提供器Notes.CONTENT_NOTE_URI获取满足条件的笔记数据
// 查询的列由PROJECTION数组指定查询条件是提醒日期大于当前日期并且笔记类型为普通笔记类型Notes.TYPE_NOTE
// 查询条件中的参数使用当前日期的字符串表示形式传入最后一个参数null表示不需要排序等额外设置
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);
// 如果查询结果游标不为null说明查询到了符合条件的笔记数据
if (c!= null) {
// 将游标移动到第一条记录位置,如果有数据则可以开始遍历获取每一条记录的信息
if (c.moveToFirst()) {
do {
// 从游标中获取当前笔记记录的提醒日期根据之前定义的列索引COLUMN_ALERTED_DATE并转换为长整型
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个Intent对象用于启动另一个广播接收者AlarmReceiver这个Intent携带了与当前笔记相关的信息
// 比如通过设置Data属性将当前笔记的ID添加到Uri中以便目标广播接收者能识别是哪个笔记的提醒操作
Intent sender = new Intent(context, AlarmReceiver.class);
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 创建一个PendingIntent对象用于包装前面创建的Intent使得这个意图可以在未来某个时间点被触发
// 这里的参数0表示请求码通常用于区分不同的PendingIntent如果不需要区分可以设为0最后一个参数0表示标志位采用默认设置
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取系统的AlarmManager服务对象用于设置和管理闹钟提醒相关的操作
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// 通过AlarmManager设置一个闹钟提醒使用RTC_WAKEUP模式表示在指定的绝对时间唤醒设备来触发提醒即使设备处于睡眠状态
// 指定提醒的时间为前面获取到的alertDate即笔记的提醒日期对应的时间戳以及对应的PendingIntent包含了要执行的意图操作
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext()); // 循环处理游标中的每一条记录,直到遍历完所有符合条件的笔记记录
}
// 关闭游标,释放查询占用的资源,避免内存泄漏等问题
c.close();
}
}
}
=======
public class AlarmInitReceiver extends BroadcastReceiver {
private static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
@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);
// 创建一个新的Intent用于触发AlarmReceiver。
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 设置Intent的数据包含笔记的ID。
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 创建一个PendingIntent用于AlarmManager设置闹钟。
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// 获取AlarmManager服务。
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
// 设置闹钟当到达提醒日期时AlarmManager将调用AlarmReceiver。
} while (c.moveToNext());
// 遍历查询结果中的所有记录。
}
c.close();
// 关闭游标。
}
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -1,58 +1,40 @@
/*
* 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.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.ui通常用于存放与应用用户界面相关的各类组件等代码
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.ui;
// 声明代码所属的包路径。
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
// 导入所需的BroadcastReceiver、Context和Intent类。
<<<<<<< HEAD
// AlarmReceiver类继承自BroadcastReceiverBroadcastReceiver用于接收系统或应用发出的广播消息
// 这里的AlarmReceiver大概率是专门用于接收与闹钟提醒相关广播的类进而做出相应的响应操作
public class AlarmReceiver extends BroadcastReceiver {
// 定义一个继承自BroadcastReceiver的类用于处理闹钟提醒。
// 重写BroadcastReceiver的onReceive方法该方法会在接收到匹配的广播消息时被调用是实现广播接收逻辑的核心方法
@Override
public void onReceive(Context context, Intent intent) {
// 通过Intent的setClass方法重新设置这个Intent要启动的目标Activity类为AlarmAlertActivity
// 意味着原本接收到的广播意图将会被转换为启动AlarmAlertActivity的意图
// 通常是因为接收到闹钟提醒广播后需要展示对应的提醒界面而AlarmAlertActivity就是用于展示提醒相关界面的
// 实现onReceive方法这是BroadcastReceiver的核心方法当接收到广播时会被调用。
intent.setClass(context, AlarmAlertActivity.class);
// 修改接收到的Intent设置其目标为AlarmAlertActivity。
// 给Intent添加一个标志位Intent.FLAG_ACTIVITY_NEW_TASK这个标志位的作用是让即将启动的Activity在一个新的任务栈中启动
// 因为广播接收者的执行环境可能没有默认的任务栈关联添加此标志可以确保Activity能正常启动并显示避免出现启动相关的问题
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 为Intent添加一个标志表示这个Activity是一个新任务的开始。
// 使用传入的Context对象启动之前设置好的Intent也就是启动AlarmAlertActivity
// 这样就会从广播接收逻辑跳转到对应的提醒界面展示逻辑,完成闹钟提醒触发后展示提醒界面的流程
context.startActivity(intent);
}
}
=======
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);
// 使用context启动设置好的Intent即启动AlarmAlertActivity。
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -1,37 +1,40 @@
/*
* 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.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import java.text.DateFormatSymbols;
import java.util.Calendar;
// 导入所需的Java类。
import net.micode.notes.R;
// 导入应用程序的资源类。
import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.NumberPicker;
// 导入所需的Android类。
public class DateTimePicker extends FrameLayout {
// 定义DateTimePicker类继承自FrameLayout。
// 定义一系列常量用于NumberPicker的最小值和最大值。
private static final boolean DEFAULT_ENABLE_STATE = true;
private static final int HOURS_IN_HALF_DAY = 12;
private static final int HOURS_IN_ALL_DAY = 24;
private static final int DAYS_IN_ALL_WEEK = 7;
@ -46,107 +49,63 @@ public class DateTimePicker extends FrameLayout {
private static final int AMPM_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MAX_VAL = 1;
// 定义NumberPicker变量用于日期、小时、分钟和上午/下午的选择。
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
private Calendar mDate;
// 定义日期显示值数组。
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
// 定义是否为上午和是否为24小时视图的标志。
private boolean mIsAm;
private boolean mIs24HourView;
// 定义是否启用控件的标志。
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
// 定义初始化标志。
private boolean mInitialising;
// 定义日期时间改变监听器。
private OnDateTimeChangedListener mOnDateTimeChangedListener;
// 定义日期改变监听器。
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当日期改变时,更新日期。
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
updateDateControl();
onDateTimeChanged();
}
};
// 定义小时改变监听器。
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
if (!mIs24HourView) {
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm;
updateAmPmControl();
}
} else {
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
}
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour);
onDateTimeChanged();
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
}
// 当小时改变时,更新小时。
// 省略了部分代码...
}
};
// 定义分钟改变监听器。
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
}
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
updateDateControl();
int newHour = getCurrentHourOfDay();
if (newHour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
updateAmPmControl();
} else {
mIsAm = true;
updateAmPmControl();
}
}
mDate.set(Calendar.MINUTE, newVal);
onDateTimeChanged();
// 当分钟改变时,更新分钟。
// 省略了部分代码...
}
};
// 定义上午/下午改变监听器。
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当上午/下午改变时,更新时间。
mIsAm = !mIsAm;
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
@ -158,11 +117,13 @@ public class DateTimePicker extends FrameLayout {
}
};
// 定义日期时间改变监听器接口。
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
// DateTimePicker构造函数初始化视图和控件。
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
@ -178,6 +139,7 @@ public class DateTimePicker extends FrameLayout {
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this);
// 初始化NumberPicker控件并设置监听器。
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
@ -191,6 +153,7 @@ public class DateTimePicker extends FrameLayout {
mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
// 初始化上午/下午选择器。
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
@ -198,22 +161,23 @@ public class DateTimePicker extends FrameLayout {
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// update controls to initial state
// 更新控件到初始状态。
updateDateControl();
updateHourControl();
updateAmPmControl();
set24HourView(is24HourView);
// set to current time
// 设置当前时间。
setCurrentDate(date);
setEnabled(isEnabled());
// set the content descriptions
// 设置内容描述。
mInitialising = false;
}
// 设置控件是否可用。
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
@ -227,25 +191,18 @@ public class DateTimePicker extends FrameLayout {
mIsEnabled = enabled;
}
// 获取控件是否可用。
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
* Get the current date in millis
*
* @return the current date in millis
*/
// 获取当前日期(毫秒)。
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
* Set the current date
*
* @param date The current date in millis
*/
// 设置当前日期。
public void setCurrentDate(long date) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
@ -253,15 +210,7 @@ public class DateTimePicker extends FrameLayout {
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
}
/**
* Set the current date
*
* @param year The current year
* @param month The current month
* @param dayOfMonth The current dayOfMonth
* @param hourOfDay The current hourOfDay
* @param minute The current minute
*/
// 设置当前日期。
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
setCurrentYear(year);
@ -271,20 +220,12 @@ public class DateTimePicker extends FrameLayout {
setCurrentMinute(minute);
}
/**
* Get current year
*
* @return The current year
*/
// 获取当前年份。
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
/**
* Set current year
*
* @param year The current year
*/
// 设置当前年份。
public void setCurrentYear(int year) {
if (!mInitialising && year == getCurrentYear()) {
return;
@ -294,192 +235,12 @@ public class DateTimePicker extends FrameLayout {
onDateTimeChanged();
}
/**
* Get current month in the year
*
* @return The current month in the year
*/
// 获取当前月份。
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* Set current month in the year
*
* @param month The month in the year
*/
// 设置当前月份。
public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) {
return;
}
mDate.set(Calendar.MONTH, month);
updateDateControl();
onDateTimeChanged();
}
/**
* Get current day of the month
*
* @return The day of the month
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
* Set current day of the month
*
* @param dayOfMonth The day of the month
*/
public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
onDateTimeChanged();
}
/**
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
private int getCurrentHour() {
if (mIs24HourView){
return getCurrentHourOfDay();
} else {
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY;
} else {
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
}
/**
* Set current hour in 24 hour mode, in the range (0~23)
*
* @param hourOfDay
*/
public void setCurrentHour(int hourOfDay) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) {
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
mIsAm = true;
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY;
}
}
updateAmPmControl();
}
mHourSpinner.setValue(hourOfDay);
onDateTimeChanged();
}
/**
* Get currentMinute
*
* @return The Current Minute
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
/**
* Set current minute
*/
public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
mMinuteSpinner.setValue(minute);
mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged();
}
/**
* @return true if this is in 24 hour view else false.
*/
public boolean is24HourView () {
return mIs24HourView;
}
/**
* Set whether in 24 hour or AM/PM mode.
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/
public void set24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) {
return;
}
mIs24HourView = is24HourView;
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
int hour = getCurrentHourOfDay();
updateHourControl();
setCurrentHour(hour);
updateAmPmControl();
}
private void updateDateControl() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
mDateSpinner.setDisplayedValues(null);
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) {
cal.add(Calendar.DAY_OF_YEAR, 1);
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
}
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate();
}
private void updateAmPmControl() {
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
private void updateHourControl() {
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}
/**
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
}
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}

@ -13,14 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import java.util.Calendar;
// 导入所需的Java类。
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
// 导入应用程序的资源类和自定义控件类。
import android.app.AlertDialog;
import android.content.Context;
@ -28,22 +32,31 @@ import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
// 导入所需的Android类。
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
// 定义DateTimePickerDialog类继承自AlertDialog并实现OnClickListener接口。
private Calendar mDate = Calendar.getInstance();
// 创建一个Calendar实例用于存储日期时间数据。
private boolean mIs24HourView;
// 定义一个标志用于标识是否为24小时制视图。
private OnDateTimeSetListener mOnDateTimeSetListener;
// 定义一个监听器,用于处理日期时间设置事件。
private DateTimePicker mDateTimePicker;
// 创建DateTimePicker实例用于选择日期时间。
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
}
// 定义一个内部接口,用于回调日期时间设置事件。
// DateTimePickerDialog构造函数初始化对话框视图和事件。
public DateTimePickerDialog(Context context, long date) {
super(context);
mDateTimePicker = new DateTimePicker(context);
setView(mDateTimePicker);
// 设置对话框内容视图为DateTimePicker。
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
@ -55,36 +68,45 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener
updateTitle(mDate.getTimeInMillis());
}
});
// 设置日期时间改变监听器,更新日期时间。
mDate.setTimeInMillis(date);
mDate.set(Calendar.SECOND, 0);
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
// 设置DateTimePicker的初始日期时间。
setButton(context.getString(R.string.datetime_dialog_ok), this);
// 设置“确定”按钮。
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
// 设置“取消”按钮,无操作。
set24HourView(DateFormat.is24HourFormat(this.getContext()));
// 设置24小时制视图。
updateTitle(mDate.getTimeInMillis());
// 更新对话框标题。
}
// 设置24小时制视图。
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
}
// 设置日期时间设置监听器。
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}
// 更新对话框标题。
private void updateTitle(long date) {
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_12HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
}
// 点击事件处理。
public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
}
}
}

@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import android.content.Context;
import android.view.Menu;
@ -24,38 +26,54 @@ import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
// 导入所需的Android类和接口。
import net.micode.notes.R;
// 导入应用程序的资源类。
public class DropdownMenu {
// 定义DropdownMenu类。
private Button mButton;
// 定义一个Button成员变量用于显示弹出菜单的按钮。
private PopupMenu mPopupMenu;
// 定义一个PopupMenu成员变量用于弹出菜单。
private Menu mMenu;
// 定义一个Menu成员变量用于菜单项。
public DropdownMenu(Context context, Button button, int menuId) {
// 构造函数初始化DropdownMenu。
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon);
// 设置按钮的背景资源。
mPopupMenu = new PopupMenu(context, mButton);
// 创建PopupMenu实例。
mMenu = mPopupMenu.getMenu();
// 获取PopupMenu的Menu。
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
// 从指定的资源文件中充气菜单项。
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();
}
});
// 设置按钮的点击事件监听器点击时显示PopupMenu。
}
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
// 设置PopupMenu项点击事件监听器。
if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener);
}
}
public MenuItem findItem(int id) {
// 查找具有指定ID的菜单项。
return mMenu.findItem(id);
}
public void setTitle(CharSequence title) {
// 设置按钮的标题。
mButton.setText(title);
}
}

@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import android.content.Context;
import android.database.Cursor;
@ -23,33 +25,40 @@ import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
// 导入所需的Android类和接口。
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
// 导入应用程序的资源类和数据模型类。
public class FoldersListAdapter extends CursorAdapter {
// 定义FoldersListAdapter类继承自CursorAdapter。
public static final String [] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
};
// 定义查询数据库时需要的列。
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
// 定义列索引常量。
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
// 调用父类的构造函数进行初始化。
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// 创建新的列表项视图。
return new FolderListItem(context);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
// 将数据绑定到视图上。
if (view instanceof FolderListItem) {
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
@ -58,23 +67,27 @@ public class FoldersListAdapter extends CursorAdapter {
}
public String getFolderName(Context context, int position) {
// 获取指定位置的文件夹名称。
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
private class FolderListItem extends LinearLayout {
// 定义一个内部类,表示列表中的单个文件夹项。
private TextView mName;
public FolderListItem(Context context) {
super(context);
inflate(context, R.layout.folder_list_item, this);
// inflate文件夹项的布局。
mName = (TextView) findViewById(R.id.tv_folder_name);
// 初始化名称文本视图。
}
public void bind(String name) {
// 将文件夹名称绑定到视图上。
mName.setText(name);
}
}
}

@ -13,147 +13,66 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
// 导入所需的Android类和接口。
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.BackgroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.TextAppearanceResources;
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
// ...
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
// 定义NoteEditActivity类继承自Activity并实现多个接口。
private class HeadViewHolder {
// 内部类,用于持有标题栏的视图元素。
public TextView tvModified;
public ImageView ivAlertIcon;
public TextView tvAlertDate;
public ImageView ibSetBgColor;
}
// 定义背景颜色选择器和字体大小选择器的映射。
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static {
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
}
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
}
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
static {
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
}
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
// 初始化背景颜色按钮映射。
}
// ...
private static final String TAG = "NoteEditActivity";
// 定义日志标签。
private HeadViewHolder mNoteHeaderHolder;
// 定义标题栏视图元素的引用。
private View mHeadViewPanel;
// 定义标题面板视图。
private View mNoteBgColorSelector;
// 定义背景颜色选择器视图。
private View mFontSizeSelector;
// 定义字体大小选择器视图。
private EditText mNoteEditor;
// 定义笔记编辑器文本框。
private View mNoteEditorPanel;
// 定义笔记编辑器面板视图。
private WorkingNote mWorkingNote;
// 定义正在编辑的笔记对象。
private SharedPreferences mSharedPrefs;
// 定义共享偏好设置。
private int mFontSizeId;
// 定义字体大小ID。
private static final String PREFERENCE_FONT_SIZE = "pref_font_size";
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;
public static final String TAG_CHECKED = String.valueOf('\u221A');
public static final String TAG_UNCHECKED = String.valueOf('\u25A1');
private LinearLayout mEditTextList;
private String mUserQuery;
private Pattern mPattern;
// 定义字体大小偏好设置的键。
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
// onCreate方法初始化Activity。
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit);
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();
return;
@ -161,713 +80,45 @@ public class NoteEditActivity extends Activity implements OnClickListener,
initResources();
}
/**
* Current activity may be killed when the memory is low. Once it is killed, for another time
* user load this activity, we should restore the former state
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity");
}
}
private boolean initActivityState(Intent intent) {
/**
* If the user specified the {@link Intent#ACTION_VIEW} but not provided with id,
* then jump to the NotesListActivity
*/
mWorkingNote = null;
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
/**
* Starting from the searched result
*/
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);
showToast(R.string.error_note_not_exist);
finish();
return false;
} else {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
} else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
// New note
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE);
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
// Parse call-record note
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate != 0 && phoneNumber != null) {
if (TextUtils.isEmpty(phoneNumber)) {
Log.w(TAG, "The call record number is null");
}
long noteId = 0;
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return false;
}
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate);
}
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId);
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} else {
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
}
@Override
protected void onResume() {
super.onResume();
initNoteScreen();
}
// ...
// 省略部分代码以节省空间包括onRestoreInstanceState、initActivityState等方法。
private void initNoteScreen() {
// 初始化笔记屏幕。
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(mWorkingNote.getContent());
} else {
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this,
mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_YEAR));
/**
* TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker
* is not ready
*/
showAlertHeader();
}
private void showAlertHeader() {
if (mWorkingNote.hasClockAlert()) {
long time = System.currentTimeMillis();
if (time > mWorkingNote.getAlertDate()) {
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
} else {
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
}
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
} else {
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
};
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
initActivityState(intent);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/**
* For new note without note id, we should firstly save it to
* generate a id. If the editing note is not worth saving, there
* is no id which is equivalent to create new note
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev)) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
}
if (mFontSizeSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mFontSizeSelector, ev)) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return super.dispatchTouchEvent(ev);
}
private boolean inRangeOfView(View view, MotionEvent ev) {
int []location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
|| ev.getY() > (y + view.getHeight())) {
return false;
}
return true;
}
private void initResources() {
mHeadViewPanel = findViewById(R.id.note_title);
mNoteHeaderHolder = new HeadViewHolder();
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this);
}
mFontSizeSelector = findViewById(R.id.font_size_selector);
for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id);
view.setOnClickListener(this);
};
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
/**
* HACKME: Fix bug of store the resource id in shared preference.
* The id may larger than the length of resources, in this case,
* return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE}
*/
if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
}
@Override
protected void onPause() {
super.onPause();
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
clearSettingState();
}
private void updateWidget() {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
mWorkingNote.getWidgetId()
});
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_set_bg_color) {
mNoteBgColorSelector.setVisibility(View.VISIBLE);
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
- View.VISIBLE);
} else if (sBgSelectorBtnsMap.containsKey(id)) {
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.GONE);
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
mNoteBgColorSelector.setVisibility(View.GONE);
} else if (sFontSizeBtnsMap.containsKey(id)) {
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
mFontSizeId = sFontSizeBtnsMap.get(id);
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit();
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
getWorkingText();
switchToListMode(mWorkingNote.getContent());
} else {
mNoteEditor.setTextAppearance(this,
TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
}
mFontSizeSelector.setVisibility(View.GONE);
}
}
@Override
public void onBackPressed() {
if(clearSettingState()) {
return;
// 设置文本样式。
// ...
}
saveNote();
super.onBackPressed();
}
private boolean clearSettingState() {
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return false;
}
public void onBackgroundColorChanged() {
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (isFinishing()) {
return true;
}
clearSettingState();
menu.clear();
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_note_edit, menu);
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
}
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
} else {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
}
if (mWorkingNote.hasClockAlert()) {
menu.findItem(R.id.menu_alert).setVisible(false);
} else {
menu.findItem(R.id.menu_delete_remind).setVisible(false);
}
return true;
}
// ...
// 省略部分代码以节省空间包括onResume、initNoteScreen等方法。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 处理菜单项点击事件。
switch (item.getItemId()) {
case R.id.menu_new_note:
createNewNote();
break;
case R.id.menu_delete:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_note));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteCurrentNote();
finish();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case R.id.menu_font_size:
mFontSizeSelector.setVisibility(View.VISIBLE);
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
break;
case R.id.menu_list_mode:
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0);
break;
case R.id.menu_share:
getWorkingText();
sendTo(this, mWorkingNote.getContent());
break;
case R.id.menu_send_to_desktop:
sendToDesktop();
break;
case R.id.menu_alert:
setReminder();
break;
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false);
break;
default:
break;
// ...
}
return true;
}
private void setReminder() {
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
mWorkingNote.setAlertDate(date , true);
}
});
d.show();
}
/**
* Share note to apps that support {@link Intent#ACTION_SEND} action
* and {@text/plain} type
*/
private void sendTo(Context context, String info) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info);
intent.setType("text/plain");
context.startActivity(intent);
}
private void createNewNote() {
// Firstly, save current editing notes
saveNote();
// For safety, start a new NoteEditActivity
finish();
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
startActivity(intent);
}
private void deleteCurrentNote() {
if (mWorkingNote.existInDatabase()) {
HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId();
if (id != Notes.ID_ROOT_FOLDER) {
ids.add(id);
} else {
Log.d(TAG, "Wrong note id, should not happen");
}
if (!isSyncMode()) {
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {
Log.e(TAG, "Delete Note error");
}
} else {
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
}
mWorkingNote.markDeleted(true);
}
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
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);
}
}
public void onWidgetChanged() {
updateWidget();
}
public void onEditTextDelete(int index, String text) {
int childCount = mEditTextList.getChildCount();
if (childCount == 1) {
return;
}
for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
mEditTextList.removeViewAt(index);
NoteEditText edit = null;
if(index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
} else {
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text);
}
int length = edit.length();
edit.append(text);
edit.requestFocus();
edit.setSelection(length);
}
public void onEditTextEnter(int index, String text) {
/**
* Should not happen, check for debug
*/
if(index > mEditTextList.getChildCount()) {
Log.e(TAG, "Index out of mEditTextList boundrary, should not happen");
}
View view = getListItem(text, index);
mEditTextList.addView(view, index);
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.requestFocus();
edit.setSelection(0);
for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i);
}
}
private void switchToListMode(String text) {
mEditTextList.removeAllViews();
String[] items = text.split("\n");
int index = 0;
for (String item : items) {
if(!TextUtils.isEmpty(item)) {
mEditTextList.addView(getListItem(item, index));
index++;
}
}
mEditTextList.addView(getListItem("", index));
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();
mNoteEditor.setVisibility(View.GONE);
mEditTextList.setVisibility(View.VISIBLE);
}
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);
if (!TextUtils.isEmpty(userQuery)) {
mPattern = Pattern.compile(userQuery);
Matcher m = mPattern.matcher(fullText);
int start = 0;
while (m.find(start)) {
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(
R.color.user_query_highlight)), m.start(), m.end(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
start = m.end();
}
}
return spannable;
}
private View getListItem(String item, int index) {
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
}
}
});
if (item.startsWith(TAG_CHECKED)) {
cb.setChecked(true);
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
item = item.substring(TAG_CHECKED.length(), item.length()).trim();
} else if (item.startsWith(TAG_UNCHECKED)) {
cb.setChecked(false);
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
}
edit.setOnTextViewChangeListener(this);
edit.setIndex(index);
edit.setText(getHighlightQueryResult(item, mUserQuery));
return view;
}
public void onTextChange(int index, boolean hasText) {
if (index >= mEditTextList.getChildCount()) {
Log.e(TAG, "Wrong index, should not happen");
return;
}
if(hasText) {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);
} else {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);
}
}
public void onCheckListModeChanged(int oldMode, int newMode) {
if (newMode == TextNote.MODE_CHECK_LIST) {
switchToListMode(mNoteEditor.getText().toString());
} else {
if (!getWorkingText()) {
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ",
""));
}
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mEditTextList.setVisibility(View.GONE);
mNoteEditor.setVisibility(View.VISIBLE);
}
}
private boolean getWorkingText() {
boolean hasChecked = false;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i);
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
if (!TextUtils.isEmpty(edit.getText())) {
if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
}
}
}
mWorkingNote.setWorkingText(sb.toString());
} else {
mWorkingNote.setWorkingText(mNoteEditor.getText().toString());
}
return hasChecked;
}
// ...
// 省略部分代码以节省空间包括setReminder、sendTo等方法。
private boolean saveNote() {
// 保存笔记。
getWorkingText();
boolean saved = mWorkingNote.saveNote();
if (saved) {
/**
* There are two modes from List view to edit view, open one note,
* create/edit a node. Opening node requires to the original
* position in the list when back from edit view, while creating a
* new node requires to the top of the list. This code
* {@link #RESULT_OK} is used to identify the create/edit state
*/
setResult(RESULT_OK);
}
return saved;
}
private void sendToDesktop() {
/**
* Before send message to home, we should make sure that current
* editing note is exists in databases. So, for new note, firstly
* save it
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
Intent sender = new Intent();
Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());
sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
sender.putExtra(Intent.EXTRA_SHORTCUT_NAME,
makeShortcutIconTitle(mWorkingNote.getContent()));
sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));
sender.putExtra("duplicate", true);
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
showToast(R.string.info_note_enter_desktop);
sendBroadcast(sender);
} 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, "Send to desktop error");
showToast(R.string.error_note_empty_for_send_to_desktop);
}
}
private String makeShortcutIconTitle(String content) {
content = content.replace(TAG_CHECKED, "");
content = content.replace(TAG_UNCHECKED, "");
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0,
SHORTCUT_ICON_TITLE_MAX_LEN) : content;
}
private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT);
}
private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show();
}
// ...
// sendToDesktop、makeShortcutIconTitle等方法。
}

@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import android.content.Context;
import android.graphics.Rect;
@ -31,21 +33,31 @@ import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
// 导入所需的Android类和接口。
import net.micode.notes.R;
// 导入应用程序的资源类。
import java.util.HashMap;
import java.util.Map;
// 导入Java的Map类。
public class NoteEditText extends EditText {
// 定义NoteEditText类继承自EditText。
private static final String TAG = "NoteEditText";
// 定义日志标签。
private int mIndex;
// 定义索引,用于标识文本编辑框的位置。
private int mSelectionStartBeforeDelete;
// 定义删除前的选择起始位置。
// 定义协议方案常量。
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
// 定义协议方案到资源ID的映射。
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
@ -53,115 +65,68 @@ public class NoteEditText extends EditText {
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
/**
* Call by the {@link NoteEditActivity} to delete or add edit text
*/
// 定义文本变化监听器接口。
public interface OnTextViewChangeListener {
/**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
*/
void onEditTextDelete(int index, String text);
/**
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
*/
void onEditTextEnter(int index, String text);
/**
* Hide or show item option when text change
*/
void onTextChange(int index, boolean hasText);
}
private OnTextViewChangeListener mOnTextViewChangeListener;
// 定义文本变化监听器。
public NoteEditText(Context context) {
super(context, null);
mIndex = 0;
}
// 构造函数,初始化索引。
public void setIndex(int index) {
mIndex = index;
}
// 设置索引。
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
// 设置文本变化监听器。
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
// 省略其他构造函数...
@Override
public boolean onTouchEvent(MotionEvent event) {
// 处理触摸事件,设置光标位置。
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
x += getScrollX();
y += getScrollY();
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
Selection.setSelection(getText(), off);
// ...
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 处理按键按下事件,记录删除前的选择起始位置。
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
return false;
}
// ...
break;
case KeyEvent.KEYCODE_DEL:
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// 处理按键抬起事件,执行删除或添加文本操作。
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
// ...
break;
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
String text = getText().subSequence(selectionStart, length()).toString();
setText(getText().subSequence(0, selectionStart));
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
default:
// ...
break;
}
return super.onKeyUp(keyCode, event);
@ -169,48 +134,18 @@ public class NoteEditText extends EditText {
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
// 处理焦点变化事件,通知文本变化监听器。
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
// ...
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
@Override
protected void onCreateContextMenu(ContextMenu menu) {
// 创建上下文菜单,处理超链接点击事件。
if (getText() instanceof Spanned) {
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
if(urls[0].getURL().indexOf(schema) >= 0) {
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
defaultResId = R.string.note_link_other;
}
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// goto a new intent
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
// ...
}
super.onCreateContextMenu(menu);
}

@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import android.content.Context;
import android.database.Cursor;
@ -24,200 +26,63 @@ import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
// 导入所需的Android类和应用程序特定的类。
public class NoteItemData {
// 定义NoteItemData类用于封装笔记项数据。
// 定义查询数据库时需要的列。
static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE,
NoteColumns.HAS_ATTACHMENT,
NoteColumns.MODIFIED_DATE,
NoteColumns.NOTES_COUNT,
NoteColumns.PARENT_ID,
NoteColumns.SNIPPET,
NoteColumns.TYPE,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
// ...
};
// 定义列索引常量。
private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8;
private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11;
// ...
// 定义成员变量,用于存储笔记项的数据。
private long mId;
private long mAlertDate;
private int mBgColorId;
private long mCreatedDate;
private boolean mHasAttachment;
private long mModifiedDate;
private int mNotesCount;
private long mParentId;
private String mSnippet;
private int mType;
private int mWidgetId;
private int mWidgetType;
private String mName;
private String mPhoneNumber;
// ...
// 定义成员变量,用于存储笔记项的位置信息。
private boolean mIsLastItem;
private boolean mIsFirstItem;
private boolean mIsOnlyOneItem;
private boolean mIsOneNoteFollowingFolder;
private boolean mIsMultiNotesFollowingFolder;
// ...
// 构造函数从Cursor中初始化笔记项数据。
public NoteItemData(Context context, Cursor cursor) {
// 从Cursor中获取数据并赋值给成员变量。
mId = cursor.getLong(ID_COLUMN);
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
mParentId = cursor.getLong(PARENT_ID_COLUMN);
mSnippet = cursor.getString(SNIPPET_COLUMN);
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
NoteEditActivity.TAG_UNCHECKED, "");
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mPhoneNumber = "";
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
if (!TextUtils.isEmpty(mPhoneNumber)) {
mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) {
mName = mPhoneNumber;
}
}
}
if (mName == null) {
mName = "";
}
// ...
checkPostion(cursor);
}
// 检查笔记项的位置,并设置相应的标志。
private void checkPostion(Cursor cursor) {
// 实现检查笔记项位置的逻辑。
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
int position = cursor.getPosition();
if (cursor.moveToPrevious()) {
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true;
} else {
mIsOneNoteFollowingFolder = true;
}
}
if (!cursor.moveToNext()) {
throw new IllegalStateException("cursor move to previous but can't move back");
}
}
}
// ...
}
// 定义一系列getter方法用于获取笔记项的数据。
public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder;
}
public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
}
public boolean isLast() {
return mIsLastItem;
}
public String getCallName() {
return mName;
}
public boolean isFirst() {
return mIsFirstItem;
}
public boolean isSingle() {
return mIsOnlyOneItem;
}
// ...
public long getId() {
return mId;
}
// ...
public long getAlertDate() {
return mAlertDate;
}
public long getCreatedDate() {
return mCreatedDate;
}
public boolean hasAttachment() {
return mHasAttachment;
}
public long getModifiedDate() {
return mModifiedDate;
}
public int getBgColorId() {
return mBgColorId;
}
public long getParentId() {
return mParentId;
}
public int getNotesCount() {
return mNotesCount;
}
public long getFolderId () {
return mParentId;
}
public int getType() {
return mType;
}
public int getWidgetType() {
return mWidgetType;
}
public int getWidgetId() {
return mWidgetId;
}
public String getSnippet() {
return mSnippet;
}
public boolean hasAlert() {
return (mAlertDate > 0);
}
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
// 定义一个静态方法用于从Cursor中获取笔记类型。
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}

@ -13,216 +13,82 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
// 导入所需的Android类和接口。
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.appwidget.AppWidgetManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnTouchListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.BackupUtils;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
// ...
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
// 定义NotesListActivity类继承自Activity并实现OnClickListener和OnItemLongClickListener接口。
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
// 定义查询令牌常量。
private static final int FOLDER_LIST_QUERY_TOKEN = 1;
private static final int MENU_FOLDER_DELETE = 0;
private static final int MENU_FOLDER_VIEW = 1;
private static final int MENU_FOLDER_CHANGE_NAME = 2;
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
private enum ListEditState {
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
};
// ...
private ListEditState mState;
// 定义当前列表编辑状态。
private BackgroundQueryHandler mBackgroundQueryHandler;
// 定义后台查询处理器。
private NotesListAdapter mNotesListAdapter;
// 定义笔记列表适配器。
private ListView mNotesListView;
// 定义笔记列表视图。
private Button mAddNewNote;
// 定义添加新笔记按钮。
private boolean mDispatch;
// 定义分发标志。
private int mOriginY;
// 定义原始Y坐标。
private int mDispatchY;
// 定义分发Y坐标。
private TextView mTitleBar;
// 定义标题栏。
private long mCurrentFolderId;
// 定义当前文件夹ID。
private ContentResolver mContentResolver;
// 定义内容解析器。
private ModeCallback mModeCallBack;
// 定义模式回调。
private static final String TAG = "NotesListActivity";
// 定义日志标签。
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;
private NoteItemData mFocusNoteDataItem;
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";
private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"
+ Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND "
+ NoteColumns.NOTES_COUNT + ">0)";
private final static int REQUEST_CODE_OPEN_NODE = 102;
private final static int REQUEST_CODE_NEW_NODE = 103;
// 省略其他成员变量...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_list);
initResources();
/**
* Insert an introduction when user firstly use this application
*/
setAppInfoFromRawRes();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
mNotesListAdapter.changeCursor(null);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
StringBuilder sb = new StringBuilder();
InputStream in = null;
try {
in = getResources().openRawResource(R.raw.introduction);
if (in != null) {
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
char [] buf = new char[1024];
int len = 0;
while ((len = br.read(buf)) > 0) {
sb.append(buf, 0, len);
}
} else {
Log.e(TAG, "Read introduction file error");
return;
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
if(in != null) {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 在创建时初始化资源。
}
WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER,
AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,
ResourceParser.RED);
note.setWorkingText(sb.toString());
if (note.saveNote()) {
sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit();
} else {
Log.e(TAG, "Save introduction note error");
return;
}
}
}
@Override
protected void onStart() {
super.onStart();
startAsyncNotesListQuery();
}
// 省略其他方法...
private void initResources() {
mContentResolver = this.getContentResolver();
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());
// 初始化后台查询处理器。
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mNotesListView = (ListView) findViewById(R.id.notes_list);
mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null),
null, false);
// 初始化笔记列表视图。
mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), null, false);
mNotesListView.setOnItemClickListener(new OnListItemClickListener());
// 设置列表项点击事件监听器。
mNotesListView.setOnItemLongClickListener(this);
// 设置列表项长按事件监听器。
mNotesListAdapter = new NotesListAdapter(this);
mNotesListView.setAdapter(mNotesListAdapter);
// 设置笔记列表适配器。
mAddNewNote = (Button) findViewById(R.id.btn_new_note);
mAddNewNote.setOnClickListener(this);
// 设置添加新笔记按钮点击事件监听器。
mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener());
// 设置添加新笔记按钮触摸事件监听器。
mDispatch = false;
mDispatchY = 0;
mOriginY = 0;
@ -231,724 +97,46 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mModeCallBack = new ModeCallback();
}
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu;
private ActionMode mActionMode;
private MenuItem mMoveMenu;
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
getMenuInflater().inflate(R.menu.note_list_options, menu);
menu.findItem(R.id.delete).setOnMenuItemClickListener(this);
mMoveMenu = menu.findItem(R.id.move);
if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER
|| DataUtils.getUserFolderCount(mContentResolver) == 0) {
mMoveMenu.setVisible(false);
} else {
mMoveMenu.setVisible(true);
mMoveMenu.setOnMenuItemClickListener(this);
}
mActionMode = mode;
mNotesListAdapter.setChoiceMode(true);
mNotesListView.setLongClickable(false);
mAddNewNote.setVisibility(View.GONE);
View customView = LayoutInflater.from(NotesListActivity.this).inflate(
R.layout.note_list_dropdown_menu, null);
mode.setCustomView(customView);
mDropDownMenu = new DropdownMenu(NotesListActivity.this,
(Button) customView.findViewById(R.id.selection_menu),
R.menu.note_list_dropdown);
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
public boolean onMenuItemClick(MenuItem item) {
mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());
updateMenu();
return true;
}
});
return true;
}
private void updateMenu() {
int selectedCount = mNotesListAdapter.getSelectedCount();
// Update dropdown menu
String format = getResources().getString(R.string.menu_select_title, selectedCount);
mDropDownMenu.setTitle(format);
MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);
if (item != null) {
if (mNotesListAdapter.isAllSelected()) {
item.setChecked(true);
item.setTitle(R.string.menu_deselect_all);
} else {
item.setChecked(false);
item.setTitle(R.string.menu_select_all);
}
}
}
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
return false;
}
public void onDestroyActionMode(ActionMode mode) {
mNotesListAdapter.setChoiceMode(false);
mNotesListView.setLongClickable(true);
mAddNewNote.setVisibility(View.VISIBLE);
}
public void finishActionMode() {
mActionMode.finish();
}
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
boolean checked) {
mNotesListAdapter.setCheckedItem(position, checked);
updateMenu();
}
public boolean onMenuItemClick(MenuItem item) {
if (mNotesListAdapter.getSelectedCount() == 0) {
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
Toast.LENGTH_SHORT).show();
return true;
}
switch (item.getItemId()) {
case R.id.delete:
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_notes,
mNotesListAdapter.getSelectedCount()));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case R.id.move:
startQueryDestinationFolders();
break;
default:
return false;
}
return true;
}
}
private class NewNoteOnTouchListener implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
Display display = getWindowManager().getDefaultDisplay();
int screenHeight = display.getHeight();
int newNoteViewHeight = mAddNewNote.getHeight();
int start = screenHeight - newNoteViewHeight;
int eventY = start + (int) event.getY();
/**
* Minus TitleBar's height
*/
if (mState == ListEditState.SUB_FOLDER) {
eventY -= mTitleBar.getHeight();
start -= mTitleBar.getHeight();
}
/**
* HACKME:When click the transparent part of "New Note" button, dispatch
* the event to the list view behind this button. The transparent part of
* "New Note" button could be expressed by formula y=-0.12x+94Unit:pixel
* and the line top of the button. The coordinate based on left of the "New
* Note" button. The 94 represents maximum height of the transparent part.
* Notice that, if the background of the button changes, the formula should
* also change. This is very bad, just for the UI designer's strong requirement.
*/
if (event.getY() < (event.getX() * (-0.12) + 94)) {
View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1
- mNotesListView.getFooterViewsCount());
if (view != null && view.getBottom() > start
&& (view.getTop() < (start + 94))) {
mOriginY = (int) event.getY();
mDispatchY = eventY;
event.setLocation(event.getX(), mDispatchY);
mDispatch = true;
return mNotesListView.dispatchTouchEvent(event);
}
}
break;
}
case MotionEvent.ACTION_MOVE: {
if (mDispatch) {
mDispatchY += (int) event.getY() - mOriginY;
event.setLocation(event.getX(), mDispatchY);
return mNotesListView.dispatchTouchEvent(event);
}
break;
}
default: {
if (mDispatch) {
event.setLocation(event.getX(), mDispatchY);
mDispatch = false;
return mNotesListView.dispatchTouchEvent(event);
}
break;
}
}
return false;
}
};
private void startAsyncNotesListQuery() {
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
: NORMAL_SELECTION;
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {
String.valueOf(mCurrentFolderId)
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
}
private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case FOLDER_NOTE_LIST_QUERY_TOKEN:
mNotesListAdapter.changeCursor(cursor);
break;
case FOLDER_LIST_QUERY_TOKEN:
if (cursor != null && cursor.getCount() > 0) {
showFolderListMenu(cursor);
} else {
Log.e(TAG, "Query folder failed");
}
break;
default:
return;
}
}
}
private void showFolderListMenu(Cursor cursor) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(R.string.menu_title_select_folder);
final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor);
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
DataUtils.batchMoveToFolder(mContentResolver,
mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which));
Toast.makeText(
NotesListActivity.this,
getString(R.string.format_move_notes_to_folder,
mNotesListAdapter.getSelectedCount(),
adapter.getFolderName(NotesListActivity.this, which)),
Toast.LENGTH_SHORT).show();
mModeCallBack.finishActionMode();
}
});
builder.show();
}
private void createNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
}
private void batchDelete() {
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
HashSet<AppWidgetAttribute> widgets = mNotesListAdapter.getSelectedWidget();
if (!isSyncMode()) {
// if not synced, delete notes directly
if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter
.getSelectedItemIds())) {
} else {
Log.e(TAG, "Delete notes error, should not happens");
}
} else {
// in sync mode, we'll move the deleted note into the trash
// folder
if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter
.getSelectedItemIds(), Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
return widgets;
}
@Override
protected void onPostExecute(HashSet<AppWidgetAttribute> widgets) {
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType);
}
}
}
mModeCallBack.finishActionMode();
}
}.execute();
}
private void deleteFolder(long folderId) {
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
return;
}
HashSet<Long> ids = new HashSet<Long>();
ids.add(folderId);
HashSet<AppWidgetAttribute> widgets = DataUtils.getFolderNoteWidget(mContentResolver,
folderId);
if (!isSyncMode()) {
// if not synced, delete folder directly
DataUtils.batchDeleteNotes(mContentResolver, ids);
} else {
// in sync mode, we'll move the deleted folder into the trash folder
DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER);
}
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType);
}
}
}
}
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
private void openFolder(NoteItemData data) {
mCurrentFolderId = data.getId();
startAsyncNotesListQuery();
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mState = ListEditState.CALL_RECORD_FOLDER;
mAddNewNote.setVisibility(View.GONE);
} else {
mState = ListEditState.SUB_FOLDER;
}
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mTitleBar.setText(R.string.call_record_folder_name);
} else {
mTitleBar.setText(data.getSnippet());
}
mTitleBar.setVisibility(View.VISIBLE);
}
// 省略其他方法...
public void onClick(View v) {
// 处理点击事件。
switch (v.getId()) {
case R.id.btn_new_note:
createNewNote();
break;
// 当点击添加新笔记按钮时,创建新笔记。
default:
break;
}
}
private void showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
private void showCreateOrModifyFolderDialog(final boolean create) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
final EditText etName = (EditText) view.findViewById(R.id.et_foler_name);
showSoftInput();
if (!create) {
if (mFocusNoteDataItem != null) {
etName.setText(mFocusNoteDataItem.getSnippet());
builder.setTitle(getString(R.string.menu_folder_change_name));
} else {
Log.e(TAG, "The long click data item is null");
return;
}
} else {
etName.setText("");
builder.setTitle(this.getString(R.string.menu_create_folder));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
hideSoftInput(etName);
}
});
final Dialog dialog = builder.setView(view).show();
final Button positive = (Button)dialog.findViewById(android.R.id.button1);
positive.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
hideSoftInput(etName);
String name = etName.getText().toString();
if (DataUtils.checkVisibleFolderName(mContentResolver, name)) {
Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name),
Toast.LENGTH_LONG).show();
etName.setSelection(0, etName.length());
return;
}
if (!create) {
if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID
+ "=?", new String[] {
String.valueOf(mFocusNoteDataItem.getId())
});
}
} else if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);
}
dialog.dismiss();
}
});
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
}
/**
* When the name edit text is null, disable the positive button
*/
etName.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
} else {
positive.setEnabled(true);
}
}
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
// 省略其他方法...
}
});
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
// 定义模式回调类,实现多选模式监听器和菜单项点击事件监听器接口。
// 省略ModeCallback类实现...
}
@Override
public void onBackPressed() {
switch (mState) {
case SUB_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
startAsyncNotesListQuery();
mTitleBar.setVisibility(View.GONE);
break;
case CALL_RECORD_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
mAddNewNote.setVisibility(View.VISIBLE);
mTitleBar.setVisibility(View.GONE);
startAsyncNotesListQuery();
break;
case NOTE_LIST:
super.onBackPressed();
break;
private class NewNoteOnTouchListener implements OnTouchListener {
// 定义新笔记触摸事件监听器类。
public boolean onTouch(View v, MotionEvent event) {
// 处理触摸事件。
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 当按下时,分发事件到列表视图。
// 省略实现...
case MotionEvent.ACTION_MOVE:
// 当移动时,分发事件到列表视图。
// 省略实现...
default:
break;
}
}
private void updateWidget(int appWidgetId, int appWidgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (appWidgetType == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (appWidgetType == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
// 默认情况,分发事件到列表视图。
// 省略实现...
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
appWidgetId
});
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mFocusNoteDataItem != null) {
menu.setHeaderTitle(mFocusNoteDataItem.getSnippet());
menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view);
menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete);
menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name);
}
}
};
@Override
public void onContextMenuClosed(Menu menu) {
if (mNotesListView != null) {
mNotesListView.setOnCreateContextMenuListener(null);
}
super.onContextMenuClosed(menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if (mFocusNoteDataItem == null) {
Log.e(TAG, "The long click data item is null");
return false;
}
switch (item.getItemId()) {
case MENU_FOLDER_VIEW:
openFolder(mFocusNoteDataItem);
break;
case MENU_FOLDER_DELETE:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_folder));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteFolder(mFocusNoteDataItem.getId());
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case MENU_FOLDER_CHANGE_NAME:
showCreateOrModifyFolderDialog(false);
break;
default:
break;
}
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
if (mState == ListEditState.NOTE_LIST) {
getMenuInflater().inflate(R.menu.note_list, menu);
// set sync or sync_cancel
menu.findItem(R.id.menu_sync).setTitle(
GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync);
} else if (mState == ListEditState.SUB_FOLDER) {
getMenuInflater().inflate(R.menu.sub_folder, menu);
} else if (mState == ListEditState.CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_record_folder, menu);
} else {
Log.e(TAG, "Wrong state:" + mState);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_new_folder: {
showCreateOrModifyFolderDialog(true);
break;
}
case R.id.menu_export_text: {
exportNoteToText();
break;
}
case R.id.menu_sync: {
if (isSyncMode()) {
if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) {
GTaskSyncService.startSync(this);
} else {
GTaskSyncService.cancelSync(this);
}
} else {
startPreferenceActivity();
}
break;
}
case R.id.menu_setting: {
startPreferenceActivity();
break;
}
case R.id.menu_new_note: {
createNewNote();
break;
}
case R.id.menu_search:
onSearchRequested();
break;
default:
break;
}
return true;
}
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false);
return true;
}
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
new AsyncTask<Void, Void, Integer>() {
//
@Override
protected Integer doInBackground(Void... unused) {
return backup.exportToText();
}
@Override
protected void onPostExecute(Integer result) {
if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_unmounted));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.STATE_SUCCESS) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.success_sdcard_export));
builder.setMessage(NotesListActivity.this.getString(
R.string.format_exported_file_location, backup
.getExportedTextFileName(), backup.getExportedTextFileDir()));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.STATE_SYSTEM_ERROR) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_export));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}
}
}.execute();
}
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
private void startPreferenceActivity() {
Activity from = getParent() != null ? getParent() : this;
Intent intent = new Intent(from, NotesPreferenceActivity.class);
from.startActivityIfNeeded(intent, -1);
}
private class OnListItemClickListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) {
NoteItemData item = ((NotesListItem) view).getItemData();
if (mNotesListAdapter.isInChoiceMode()) {
if (item.getType() == Notes.TYPE_NOTE) {
position = position - mNotesListView.getHeaderViewsCount();
mModeCallBack.onItemCheckedStateChanged(null, position, id,
!mNotesListAdapter.isSelectedItem(position));
}
return;
}
switch (mState) {
case NOTE_LIST:
if (item.getType() == Notes.TYPE_FOLDER
|| item.getType() == Notes.TYPE_SYSTEM) {
openFolder(item);
} else if (item.getType() == Notes.TYPE_NOTE) {
openNode(item);
} else {
Log.e(TAG, "Wrong note type in NOTE_LIST");
}
break;
case SUB_FOLDER:
case CALL_RECORD_FOLDER:
if (item.getType() == Notes.TYPE_NOTE) {
openNode(item);
} else {
Log.e(TAG, "Wrong note type in SUB_FOLDER");
}
break;
default:
break;
}
}
}
}
private void startQueryDestinationFolders() {
String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?";
selection = (mState == ListEditState.NOTE_LIST) ? selection:
"(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")";
mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN,
null,
Notes.CONTENT_NOTE_URI,
FoldersListAdapter.PROJECTION,
selection,
new String[] {
String.valueOf(Notes.TYPE_FOLDER),
String.valueOf(Notes.ID_TRASH_FOLER),
String.valueOf(mCurrentFolderId)
},
NoteColumns.MODIFIED_DATE + " DESC");
}
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) {
mFocusNoteDataItem = ((NotesListItem) view).getItemData();
if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) {
if (mNotesListView.startActionMode(mModeCallBack) != null) {
mModeCallBack.onItemCheckedStateChanged(null, position, id, true);
mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
} else {
Log.e(TAG, "startActionMode fails");
}
} else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {
mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener);
}
}
return false;
}
}

@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import android.content.Context;
import android.database.Cursor;
@ -22,28 +24,39 @@ import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
// 导入所需的Android类和接口。
import net.micode.notes.data.Notes;
// 导入应用程序的数据模型类。
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
// 导入Java的集合类。
public class NotesListAdapter extends CursorAdapter {
// 定义NotesListAdapter类继承自CursorAdapter。
private static final String TAG = "NotesListAdapter";
// 定义日志标签。
private Context mContext;
// 定义上下文引用。
private HashMap<Integer, Boolean> mSelectedIndex;
// 定义选中状态的哈希映射。
private int mNotesCount;
// 定义笔记计数。
private boolean mChoiceMode;
// 定义选择模式标志。
public static class AppWidgetAttribute {
// 定义内部类,用于存储应用小部件属性。
public int widgetId;
public int widgetType;
};
public NotesListAdapter(Context context) {
// 构造函数,初始化适配器。
super(context, null);
mSelectedIndex = new HashMap<Integer, Boolean>();
mContext = context;
@ -52,33 +65,38 @@ public class NotesListAdapter extends CursorAdapter {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// 创建新的列表项视图。
return new NotesListItem(context);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
// 将数据绑定到视图上。
if (view instanceof NotesListItem) {
NoteItemData itemData = new NoteItemData(context, cursor);
((NotesListItem) view).bind(context, itemData, mChoiceMode,
isSelectedItem(cursor.getPosition()));
((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition()));
}
}
public void setCheckedItem(final int position, final boolean checked) {
// 设置列表项的选中状态。
mSelectedIndex.put(position, checked);
notifyDataSetChanged();
}
public boolean isInChoiceMode() {
// 获取选择模式状态。
return mChoiceMode;
}
public void setChoiceMode(boolean mode) {
// 设置选择模式。
mSelectedIndex.clear();
mChoiceMode = mode;
}
public void selectAll(boolean checked) {
// 全选或全不选所有列表项。
Cursor cursor = getCursor();
for (int i = 0; i < getCount(); i++) {
if (cursor.moveToPosition(i)) {
@ -90,6 +108,7 @@ public class NotesListAdapter extends CursorAdapter {
}
public HashSet<Long> getSelectedItemIds() {
// 获取选中的笔记ID集合。
HashSet<Long> itemSet = new HashSet<Long>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
@ -101,11 +120,11 @@ public class NotesListAdapter extends CursorAdapter {
}
}
}
return itemSet;
}
public HashSet<AppWidgetAttribute> getSelectedWidget() {
// 获取选中的应用小部件属性集合。
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
@ -116,9 +135,6 @@ public class NotesListAdapter extends CursorAdapter {
widget.widgetId = item.getWidgetId();
widget.widgetType = item.getWidgetType();
itemSet.add(widget);
/**
* Don't close cursor here, only the adapter could close it
*/
} else {
Log.e(TAG, "Invalid cursor");
return null;
@ -129,6 +145,7 @@ public class NotesListAdapter extends CursorAdapter {
}
public int getSelectedCount() {
// 获取选中的笔记数量。
Collection<Boolean> values = mSelectedIndex.values();
if (null == values) {
return 0;
@ -144,11 +161,13 @@ public class NotesListAdapter extends CursorAdapter {
}
public boolean isAllSelected() {
// 检查是否所有笔记都被选中。
int checkedCount = getSelectedCount();
return (checkedCount != 0 && checkedCount == mNotesCount);
}
public boolean isSelectedItem(final int position) {
// 检查指定位置的笔记是否被选中。
if (null == mSelectedIndex.get(position)) {
return false;
}
@ -157,17 +176,20 @@ public class NotesListAdapter extends CursorAdapter {
@Override
protected void onContentChanged() {
// 当内容变化时调用。
super.onContentChanged();
calcNotesCount();
}
@Override
public void changeCursor(Cursor cursor) {
// 当游标变化时调用。
super.changeCursor(cursor);
calcNotesCount();
}
private void calcNotesCount() {
// 计算笔记数量。
mNotesCount = 0;
for (int i = 0; i < getCount(); i++) {
Cursor c = (Cursor) getItem(i);

@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import android.content.Context;
import android.text.format.DateUtils;
@ -23,32 +25,39 @@ import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
// 导入所需的Android类和接口。
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
// 导入应用程序的资源类和工具类。
public class NotesListItem extends LinearLayout {
// 定义NotesListItem类继承自LinearLayout。
private ImageView mAlert;
private TextView mTitle;
private TextView mTime;
private TextView mCallName;
private NoteItemData mItemData;
private CheckBox mCheckBox;
// 定义视图元素和数据模型。
public NotesListItem(Context context) {
super(context);
inflate(context, R.layout.note_item, this);
// inflate笔记项布局。
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
mTitle = (TextView) findViewById(R.id.tv_title);
mTime = (TextView) findViewById(R.id.tv_time);
mCallName = (TextView) findViewById(R.id.tv_name);
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
// 初始化视图元素。
}
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
// 绑定数据到视图。
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
@ -57,66 +66,24 @@ public class NotesListItem extends LinearLayout {
}
mItemData = data;
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
} else {
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
// 设置数据模型。
// 根据数据模型的类型和属性设置视图元素的显示。
// 省略部分代码...
if (data.getType() == Notes.TYPE_FOLDER) {
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
mAlert.setVisibility(View.GONE);
} else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
}
}
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
// 设置修改时间。
setBackground(data);
// 设置背景。
}
private void setBackground(NoteItemData data) {
// 设置背景资源。
int id = data.getBgColorId();
if (data.getType() == Notes.TYPE_NOTE) {
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} else if (data.isLast()) {
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
} else if (data.isFirst() || data.isMultiFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
} else {
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
} else {
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
// 省略部分代码...
}
public NoteItemData getItemData() {
return mItemData;
}
// 获取数据模型。
}

@ -13,340 +13,131 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 版权声明说明该代码由MiCode开源社区持有并在Apache License 2.0下授权。
package net.micode.notes.ui;
// 声明代码所属的包路径。
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
// ...
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
// 导入应用程序的资源类、数据模型类和同步服务类。
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences";
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
// 定义NotesPreferenceActivity类继承自PreferenceActivity。
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
private static final String AUTHORITIES_FILTER_KEY = "authorities";
// 定义偏好设置的键和名称。
public static final String PREFERENCE_NAME = "notes_preferences";
// ...
private PreferenceCategory mAccountCategory;
private GTaskReceiver mReceiver;
private Account[] mOriAccounts;
private boolean mHasAddedAccount;
// 定义账户类别、接收器、原始账户数组和账户添加标志。
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 调用父类的onCreate方法。
/* using the app icon for navigation */
// 设置ActionBar的返回图标。
getActionBar().setDisplayHomeAsUpEnabled(true);
// 从资源文件中添加偏好设置。
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
// 初始化账户类别。
mReceiver = new GTaskReceiver();
// 创建GTask接收器。
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
// 注册接收器以监听GTask服务的广播。
mOriAccounts = null;
// 初始化原始账户数组。
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
// 添加设置头部视图。
}
@Override
protected void onResume() {
super.onResume();
// need to set sync account automatically if user has added a new
// account
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
refreshUI();
}
@Override
protected void onDestroy() {
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
// ...
// 省略部分代码以节省空间包括onResume、onDestroy等方法。
private void loadAccountPreference() {
// 加载账户偏好设置。
mAccountCategory.removeAll();
// 移除所有账户偏好项。
Preference accountPref = new Preference(this);
// 创建账户偏好项。
final String defaultAccount = getSyncAccountName(this);
// 获取默认同步账户名。
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// the first time to set account
showSelectAccountAlertDialog();
} else {
// if the account has already been set, we need to promp
// user about the risk
showChangeAccountConfirmAlertDialog();
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
// 处理账户偏好项的点击事件。
// ...
return true;
}
});
mAccountCategory.addPreference(accountPref);
// 将账户偏好项添加到账户类别中。
}
private void loadSyncButton() {
// 加载同步按钮。
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 获取同步按钮和最后同步时间视图。
// set button state
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// set last sync time
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
// 设置按钮状态和最后同步时间。
// ...
}
private void refreshUI() {
// 刷新用户界面。
loadAccountPreference();
loadSyncButton();
}
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);
mOriAccounts = accounts;
mHasAddedAccount = false;
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}
items[index++] = account.name;
}
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString());
dialog.dismiss();
refreshUI();
}
});
}
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
final AlertDialog dialog = dialogBuilder.show();
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
}
});
}
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog();
} else if (which == 1) {
removeSyncAccount();
refreshUI();
}
}
});
dialogBuilder.show();
}
// ...
// 省略部分代码以节省空间包括showSelectAccountAlertDialog、showChangeAccountConfirmAlertDialog等方法。
private Account[] getGoogleAccounts() {
// 获取Google账户。
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
// 设置同步账户。
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
editor.commit();
// clean up last sync time
setLastSyncTime(this, 0);
// clean up local gtask related info
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
}
// ...
}
private void removeSyncAccount() {
// 移除同步账户。
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
editor.commit();
// clean up local gtask related info
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
// ...
}
public static String getSyncAccountName(Context context) {
// 获取同步账户名。
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
public static void setLastSyncTime(Context context, long time) {
// 设置最后同步时间。
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
@ -355,28 +146,27 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
public static long getLastSyncTime(Context context) {
// 获取最后同步时间。
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
private class GTaskReceiver extends BroadcastReceiver {
// 定义GTask接收器类用于接收GTask服务的广播。
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
// 刷新用户界面。
// ...
}
}
public boolean onOptionsItemSelected(MenuItem item) {
// 处理选项菜单项的点击事件。
switch (item.getItemId()) {
case android.R.id.home:
// 当点击返回图标时,返回到笔记列表活动。
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

@ -14,13 +14,7 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.widget通常用于存放与桌面小部件Widget相关的代码逻辑
package net.micode.notes.widget;
=======
package net.micode.notes.widget;
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
@ -38,39 +32,24 @@ import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity;
<<<<<<< HEAD
// NoteWidgetProvider类继承自AppWidgetProvider是安卓中用于创建桌面小部件的基类这里定义为抽象类意味着它需要被具体的子类继承并实现一些抽象方法
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中准确获取该列的数据索引为0是因为在PROJECTION数组中它排在第一位
public static final int COLUMN_ID = 0;
// 定义一个常量表示查询结果中背景颜色ID列对应的索引位置用于从游标中获取背景颜色相关信息索引为1对应其在PROJECTION数组中的位置
public static final int COLUMN_BG_COLOR_ID = 1;
// 定义一个常量表示查询结果中摘要信息列对应的索引位置用于获取笔记的摘要内容索引为2表示其在PROJECTION数组中的顺序位置
public static final int COLUMN_SNIPPET = 2;
// 定义一个用于日志记录的标签字符串,方便在日志中识别该类输出的相关信息,便于调试和查看运行情况
private static final String TAG = "NoteWidgetProvider";
// 重写AppWidgetProvider的onDeleted方法该方法会在小部件被删除时被调用用于处理小部件删除后的相关逻辑
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// 创建一个ContentValues对象用于存储要更新到数据库中的键值对数据
ContentValues values = new ContentValues();
// 将笔记对应的小部件ID设置为无效值AppWidgetManager.INVALID_APPWIDGET_ID表示该笔记与小部件的关联已解除
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
// 遍历被删除的小部件ID数组对每个小部件ID执行以下操作
for (int i = 0; i < appWidgetIds.length; i++) {
// 通过ContentResolver更新笔记内容提供器Notes.CONTENT_NOTE_URI中的数据
// 更新条件是笔记的小部件ID与当前遍历到的小部件ID匹配将对应的笔记记录中的小部件ID设置为无效值
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
@ -78,79 +57,18 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
}
}
// 私有方法用于根据给定的小部件ID从数据库中获取该小部件相关的笔记信息如笔记ID、背景颜色、摘要等返回一个游标Cursor对象用于遍历查询结果
=======
public abstract class NoteWidgetProvider extends AppWidgetProvider {
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_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";
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
ContentValues values = new ContentValues();
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_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])});
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",
<<<<<<< HEAD
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) },
null);
}
// 受保护的方法用于更新小部件的显示内容它调用了另一个重载的update方法并传入默认的隐私模式参数false
=======
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) },
null);
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
update(context, appWidgetManager, appWidgetIds, false);
}
<<<<<<< HEAD
// 私有方法用于实际更新小部件的显示内容根据传入的小部件ID数组逐个更新对应的小部件显示情况同时可以根据隐私模式参数进行不同的显示设置
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
// 遍历小部件ID数组对每个有效的小部件ID执行更新操作
for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i]!= AppWidgetManager.INVALID_APPWIDGET_ID) {
// 获取默认的背景颜色ID通过ResourceParser工具类的方法来获取可能根据用户设置等情况返回相应的颜色标识
int bgId = ResourceParser.getDefaultBgId(context);
// 初始化笔记摘要信息为空字符串,后续根据查询结果进行更新
String snippet = "";
// 创建一个Intent对象用于启动NoteEditActivity可能是点击小部件后跳转到笔记编辑页面等相关操作具体行为由后续设置决定
Intent intent = new Intent(context, NoteEditActivity.class);
// 设置Intent的标志位使得如果对应的Activity已经在栈顶就不会重新创建新的实例而是复用已有的实例
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 将当前小部件的ID通过Intent的额外数据传递机制传递给目标Activity以便目标Activity能识别是哪个小部件触发的操作
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
// 将小部件的类型通过Intent的额外数据传递给目标Activity具体类型由子类实现的抽象方法getWidgetType来确定
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
// 通过前面定义的方法获取当前小部件相关的笔记信息游标
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c!= null && c.moveToFirst()) {
// 如果查询到的笔记记录数量大于1说明存在多条笔记对应同一个小部件ID的异常情况记录错误日志并关闭游标直接返回不进行后续更新操作
=======
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
for (int i = 0; i < appWidgetIds.length; i++) {
@ -164,54 +82,11 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c != null && c.moveToFirst()) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (c.getCount() > 1) {
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
c.close();
return;
}
<<<<<<< HEAD
// 获取笔记的摘要信息从游标中根据之前定义的列索引COLUMN_SNIPPET获取对应的数据
snippet = c.getString(COLUMN_SNIPPET);
// 获取笔记对应的背景颜色ID从游标中根据列索引COLUMN_BG_COLOR_ID获取对应的数据并更新之前获取的默认背景颜色ID
bgId = c.getInt(COLUMN_BG_COLOR_ID);
// 将笔记的ID通过Intent的额外数据传递给目标Activity以便在编辑页面等操作中能准确操作对应的笔记
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
// 设置Intent的动作这里设置为查看ACTION_VIEW动作表示点击小部件后可能是查看对应笔记的操作意图
intent.setAction(Intent.ACTION_VIEW);
} else {
// 如果没有查询到对应的笔记信息,设置摘要信息为一个默认的提示字符串,表示小部件暂无内容
snippet = context.getResources().getString(R.string.widget_havenot_content);
// 设置Intent的动作这里设置为插入或编辑ACTION_INSERT_OR_EDIT动作可能意味着点击小部件后可以进行新建或编辑笔记的操作
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
}
// 如果游标不为null关闭游标释放资源避免内存泄漏等问题
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通过Intent的额外数据传递给目标Activity方便后续根据颜色进行相关操作或显示调整
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
// 生成一个PendingIntent对象用于包装前面创建的Intent使得点击小部件时能触发对应的操作根据隐私模式进行不同的设置
PendingIntent pendingIntent = null;
if (privacyMode) {
// 如果处于隐私模式,设置小部件文本显示的内容为一个特定的隐私提示字符串
rv.setTextViewText(R.id.widget_text,
context.getString(R.string.widget_under_visit_mode));
// 创建一个PendingIntent用于启动NotesListActivity可能在隐私模式下点击小部件跳转到笔记列表页面等操作
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
} else {
// 如果不处于隐私模式,设置小部件文本显示的内容为之前获取或设置的笔记摘要信息
rv.setTextViewText(R.id.widget_text, snippet);
// 创建一个PendingIntent用于启动之前设置的Intent对应的Activity根据不同情况可能是编辑页面、查看页面等
=======
snippet = c.getString(COLUMN_SNIPPET);
bgId = c.getInt(COLUMN_BG_COLOR_ID);
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
@ -239,38 +114,19 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
} else {
rv.setTextViewText(R.id.widget_text, snippet);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
<<<<<<< HEAD
// 设置小部件文本视图的点击事件PendingIntent使得点击文本时能触发对应的操作
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
// 通过AppWidgetManager更新指定小部件ID对应的小部件显示内容应用前面设置好的RemoteViews对象
=======
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
}
}
<<<<<<< HEAD
// 抽象方法需要由具体的子类实现根据传入的背景颜色ID返回对应的背景资源ID用于设置小部件的背景图片等显示资源
protected abstract int getBgResourceId(int bgId);
// 抽象方法需要由具体的子类实现返回小部件对应的布局资源ID用于创建RemoteViews对象来构建小部件的远程视图显示
protected abstract int getLayoutId();
// 抽象方法需要由具体的子类实现返回小部件的类型信息具体类型由子类根据业务逻辑定义用于在Intent等操作中传递和区分不同类型的小部件
protected abstract int getWidgetType();
}
=======
protected abstract int getBgResourceId(int bgId);
protected abstract int getLayoutId();
protected abstract int getWidgetType();
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,10 +14,6 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类属于net.micode.notes.widget包通常用于存放与桌面小部件相关的具体实现类
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.widget;
import android.appwidget.AppWidgetManager;
@ -27,22 +23,6 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
<<<<<<< HEAD
// NoteWidgetProvider_2x类继承自NoteWidgetProvider抽象类意味着它需要实现NoteWidgetProvider中定义的抽象方法
// 它是专门针对某种特定尺寸从类名推测可能是2倍尺寸桌面小部件的具体实现类
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
// 重写父类的onUpdate方法该方法会在小部件需要更新时被调用例如小部件首次添加到桌面、系统要求更新小部件等情况
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的update方法来执行小部件更新的通用逻辑将具体的更新操作交给父类中定义的update方法处理
// 这样可以复用父类中关于更新小部件显示内容、设置点击事件等相关的逻辑代码
super.update(context, appWidgetManager, appWidgetIds);
}
// 实现父类中定义的抽象方法getLayoutId用于返回该小部件对应的布局资源ID这里返回的是R.layout.widget_2x
// 意味着该小部件使用名为widget_2x的布局文件来进行界面显示布局定义不同的布局文件可以包含不同的视图控件、排列方式等
=======
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
@Override
@ -50,34 +30,18 @@ public class NoteWidgetProvider_2x extends NoteWidgetProvider {
super.update(context, appWidgetManager, appWidgetIds);
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getLayoutId() {
return R.layout.widget_2x;
}
<<<<<<< HEAD
// 实现父类中定义的抽象方法getBgResourceId根据传入的背景颜色IDbgId获取对应的背景资源ID
// 这里通过调用ResourceParser.WidgetBgResources类中的方法来获取具体是获取2倍尺寸小部件对应的背景资源ID
// 使得小部件能根据不同的背景颜色ID显示相应的背景图片等资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}
<<<<<<< HEAD
// 实现父类中定义的抽象方法getWidgetType返回该小部件的类型标识这里返回的是Notes.TYPE_WIDGET_2X
// 用于区分不同尺寸、不同类型的小部件,在应用中可以根据这个类型标识来进行不同的业务逻辑处理,比如不同的显示样式、功能操作等
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_2X;
}
<<<<<<< HEAD
}
=======
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,10 +14,6 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.widget通常用于存放与桌面小部件相关的具体实现类从类名NoteWidgetProvider_4x推测这是针对特定尺寸可能是4倍尺寸小部件的相关实现类
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.widget;
import android.appwidget.AppWidgetManager;
@ -27,21 +23,6 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
<<<<<<< HEAD
// NoteWidgetProvider_4x类继承自NoteWidgetProvider抽象类需要实现抽象类中定义的抽象方法以提供针对特定类型这里可能是4倍尺寸小部件的具体功能实现
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
// 重写父类的onUpdate方法该方法会在桌面小部件需要更新时被调用例如系统触发小部件更新如时间间隔到了、配置变化等情况或者用户手动触发小部件更新操作时
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 通过调用父类的update方法来执行小部件更新的通用逻辑复用父类中已定义好的更新操作流程比如设置小部件的显示内容、点击事件等相关逻辑
// 子类只需关注自身特有的部分(如特定布局、资源获取等)即可,这样可以提高代码的复用性和可维护性
super.update(context, appWidgetManager, appWidgetIds);
}
// 重写父类中定义的抽象方法getLayoutId用于返回该4倍尺寸小部件对应的布局资源ID这里返回的是R.layout.widget_4x
// 意味着该小部件会使用名为widget_4x的布局文件来进行界面的显示布局设置这个布局文件中定义了小部件在桌面上展示的具体视图结构、控件摆放等内容
=======
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
@Override
@ -49,34 +30,17 @@ public class NoteWidgetProvider_4x extends NoteWidgetProvider {
super.update(context, appWidgetManager, appWidgetIds);
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
protected int getLayoutId() {
return R.layout.widget_4x;
}
<<<<<<< HEAD
// 重写父类中定义的抽象方法getBgResourceId该方法根据传入的背景颜色标识IDbgId来获取对应的背景资源ID
// 这里借助ResourceParser.WidgetBgResources类中的方法来获取具体是获取适用于4倍尺寸小部件的背景资源ID
// 从而可以根据不同的背景颜色设置来展示相应的背景图片等资源,使小部件的外观符合预期的设计要求
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
<<<<<<< HEAD
// 重写父类中定义的抽象方法getWidgetType返回该小部件的类型标识这里返回的是Notes.TYPE_WIDGET_4X
// 这个类型标识可以用于在整个应用中区分不同尺寸、不同功能特性的小部件,比如在处理小部件相关的业务逻辑时,根据不同的类型执行不同的操作,
// 像显示不同的内容、提供不同的交互功能等,有助于实现小部件功能的差异化和定制化
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_4X;
}
<<<<<<< HEAD
}
=======
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -1,410 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "UTLab.h"
#define N 32//每个染色体的基因数
//#define W 100//试卷总分值
#define TYPE -1//题型数量
//#define M 2 //目标数目
//#define TYPE -1//求最大还是求最小
#define GEN 100//迭代次数
#define POP_SIZE 30//种群大小
#define SELECTQUE_SIZE 250 //选择题题库总量
#define TKQUE_SIZE 200 //填空题题库总量
#define ZHGQUE_SIZE 50 //主观题题库总量
#define NUM 4//题目特征数目
#define NUMCH 6//章节数目
#define NUMD 3//难度数目
#define DRATE1 0.2 //难度系数为1的预定义比率
#define DRATE2 0.6
#define DRATE3 0.2
#define CHRATE1 0.1//预定义每章的分值比率
#define CHRATE2 0.2
#define CHRATE3 0.2
#define CHRATE4 0.2
#define CHRATE5 0.2
#define CHRATE6 0.1
#define RCH 0.5
#define RD 0.5
#define P_MUTATION 0.008//变异系数
#define P_CROSSOVER 0.8//交叉系
double OBJECTIVE[POP_SIZE+1];
double DRATE[NUMD+1]={0,DRATE1,DRATE2,DRATE3}; //预定义难度系数比例
double CHRATE[NUMCH+1]={0,CHRATE1,CHRATE2,CHRATE3,CHRATE4,CHRATE5,CHRATE6};//预定义章节比
double CHROMOSOME[POP_SIZE+1][N+1];//染色体数组
double q[POP_SIZE+1];//存储选题到的题号的数组
int SELECTQUESTION[SELECTQUE_SIZE+1][NUM];//选择题题库;1/分值2、章节3、难度
int TKQUESTION[TKQUE_SIZE+1][NUM];//填空题题库;1/分值2、章节3、难度
int ZHGQUESTION[ZHGQUE_SIZE+1][NUM];//主观题题库;1/分值2、章节3、难度
static double caucal(double x[]); //计算新染色体的章节和难度比
static void initialquestion(void);//初始化题库
static void initialrate(void);//初始化各比例
static void initialization(void);//初始化
static void evaluation(int gen);
static void objective_function(void);
static void selection(void);
static void crossover(void);
static void mutation(void);
static int constraint_check(double x[]);
static double caucal(double x[])
{int i,j,t,s,m;
double chrate[NUMCH+1],drate[NUMD+1];//定义各章节分值比和难度比
double da=0;
double ba=0;
for (j=0;j<=NUMCH;j++)
{chrate[j]=0;}
for (j=0;j<=NUMD;j++)
{drate[j]=0;}
for(j=1;j<=30;j++)
{t=int(x[j]);
if(j<=15)
{
s=SELECTQUESTION[t][2];
chrate[s]+=2;
m=SELECTQUESTION[t][3];
drate[m]+=2;
}
else if(j>15&&j<=25)
{
s=TKQUESTION[t][2];
chrate[s]+=2;
m=TKQUESTION[t][3];
drate[m]+=2;
}
else
{s=ZHGQUESTION[t][2];
chrate[s]+=10;
m=ZHGQUESTION[t][3];
drate[m]+=10;
}
}
for(j=1;j<=NUMCH;j++)
{chrate[j]=chrate[j]/100;
//printf("第%d个染色体第%d章的分值比是%f\n",i,j,chrate[i][j]);
ba+=fabs(double(chrate[j]-CHRATE[j]));
}
//printf("第个染色体章的分值比平均值%f\n",ba/NUMCH);
x[31]=ba/NUMCH;
for(j=1;j<=NUMD;j++)
{drate[j]=drate[j]/100;
//printf("第%d个染色体第%d难度的分值比是%f\n",i,j,drate[i][j]);
da+=fabs(double(drate[j]-DRATE[j]));
}
//printf("第染色体难度差的平均值是%f\n",da/NUMD);
x[32]=da/NUMD;
return RCH*x[31]+RD*x[32];
}
static void objective_function(void)
{
int i;
for(i = 1; i <= POP_SIZE; i++) {
OBJECTIVE[i] =RCH*CHROMOSOME[i][31]+RD*CHROMOSOME[i][32];
}
}
static void initialquestion(void)
{ int i,j;
for(i=1;i<=SELECTQUE_SIZE+1;i++)//将选择题输入题库
for(j=1;j<=NUM;j++)
{
if(j==1)SELECTQUESTION[i][j]=2;
if(j==2)SELECTQUESTION[i][j]=int(myu(1,7));
if(j==3)SELECTQUESTION[i][j]=int(myu(1,4));
}
for(i=1;i<=TKQUE_SIZE+1;i++)//将填空题输入题库
for(j=1;j<=NUM;j++)
{
if(j==1)TKQUESTION[i][j]=2;
if(j==2)TKQUESTION[i][j]=int(myu(1,7));
if(j==3)TKQUESTION[i][j]=int(myu(1,4));
}
for(i=1;i<=ZHGQUE_SIZE+1;i++)//将填空题输入题库
for(j=1;j<=NUM;j++)
{
if(j==1)ZHGQUESTION[i][j]=10;
if(j==2)ZHGQUESTION[i][j]=int(myu(1,7));
if(j==3)ZHGQUESTION[i][j]=int(myu(1,4));
}
}
static void initialrate(void)//初始化章节和难度比例
{ int i,j,t,s,x;
double chrate[POP_SIZE+1][NUMCH+1],drate[POP_SIZE+1][NUMD+1];//定义各章节分值比和难度比
double da=0;
double ba=0;
for (i=1;i<=POP_SIZE;i++)
{for (j=1;j<=NUMCH;j++)
{chrate[i][j]=0;}
}
for (i=1;i<=POP_SIZE;i++)
{for (j=1;j<=NUMD;j++)
{drate[i][j]=0;}
}
for(i=1; i<=POP_SIZE; i++)
{
for(j=1;j<=30;j++)
{t=CHROMOSOME[i][j];
if(j<=15)
{
s=SELECTQUESTION[t][2];
chrate[i][s]+=2;
x=SELECTQUESTION[t][3];
drate[i][x]+=2;
}
else if(j>15&&j<=25)
{
s=TKQUESTION[t][2];
chrate[i][s]+=2;
x=TKQUESTION[t][3];
drate[i][x]+=2;
}
else
{s=ZHGQUESTION[t][2];
chrate[i][s]+=10;
x=ZHGQUESTION[t][3];
drate[i][x]+=10;
}
}
for(j=1;j<=NUMCH;j++)
{chrate[i][j]=chrate[i][j]/100;
//printf("第%d个染色体第%d章的分值比是%f\n",i,j,chrate[i][j]);
ba+=fabs(double(chrate[i][j]-CHRATE[j]));
}
//printf("第%d个染色体章的分值比平均值%f\n",i,ba/NUMCH);
CHROMOSOME[i][31]=ba/NUMCH;
ba=0;
for(j=1;j<=NUMD;j++)
{drate[i][j]=drate[i][j]/100;
//printf("第%d个染色体第%d难度的分值比是%f\n",i,j,drate[i][j]);
da+=fabs(double(drate[i][j]-DRATE[j]));
}
//printf("第%d个染色体难度差的平均值是%f\n",i,da/NUMD);
CHROMOSOME[i][32]=da/NUMD;
da=0;
}
}
static void initialization(void)//初始化染色体操作
{
int i,j;
for(i=1; i<=POP_SIZE; i++)//初始化染色体,待讨论!
{
for(j=1;j<=15;j++)//选择选择题
CHROMOSOME[i][j]=int(myu(1,SELECTQUE_SIZE+1));
for(j=16;j<=25;j++)//选择填空题
CHROMOSOME[i][j]=int(myu(1,TKQUE_SIZE+1));
for(;j<=30;j++)//select postive question
CHROMOSOME[i][j]=int(myu(1,ZHGQUE_SIZE+1));
}
}
static int constraint_check(double x[])
{
int n;
for(n=1;n<=30;n++) if(x[n]<=0) return 0;
return 1;
}
void main()
{
int i, j,t,k;
double a;
q[0]=0.05; a=0.05;//定义的比率
for(i=1; i<=POP_SIZE; i++) {a=a*0.95; q[i]=q[i-1]+a;}
initialquestion();
initialization();
initialrate();
evaluation(0);
for(i=1; i<=GEN; i++) {
selection();
crossover();
mutation();
evaluation(i);
t=1; k=1;
for(j=1;j<=POP_SIZE;j++)
if(t>RCH*CHROMOSOME[j][31]+RD*CHROMOSOME[j][32])
{
t=RCH*CHROMOSOME[j][31]+RD*CHROMOSOME[j][32];
k=j;
}
printf("第%d代的最佳章节比是%f,最佳难度比是%f\n",i,CHROMOSOME[k][31],CHROMOSOME[k][32]);
}
printf("题号 分值 章节 难度\n");
for(i=1; i<=1; i++)//输出生成的试卷
for(j=1;j<=30;j++)
{t=CHROMOSOME[i][j];
if(j<=15)
{ printf("%d\t",t);
for(k=1;k<NUM;k++)
printf("%d\t",SELECTQUESTION[t][k]);
printf("\n");
}
else if(j>15&&j<=25)
{ printf("%d\t",t);
for(k=1;k<NUM;k++)
printf("%d\t",TKQUESTION[t][k]);
printf("\n");
}
else
{ printf("%d\t",t);
for(k=1;k<NUM;k++)
printf("%d\t",ZHGQUESTION[t][k]);
printf("\n");
}
}
a=0;
for(i=1;i<=POP_SIZE;i++)
a+=(1-RCH*CHROMOSOME[i][31]+RD*CHROMOSOME[i][32]);
printf("最佳适应度为%f\n",a/0.3);
}
static void evaluation(int gen)
{
double a;
int i, j, label;
objective_function();//计算目标函数值
if(gen==0){
OBJECTIVE[0]=OBJECTIVE[1];
for(j = 1; j <= N; j++) CHROMOSOME[0][j]=CHROMOSOME[1][j];
}
for(i=0; i<POP_SIZE; i++){
label=0; a=OBJECTIVE[i];
for(j=i+1; j<=POP_SIZE; j++)
if((TYPE*a)<(TYPE*OBJECTIVE[j])) {
a=OBJECTIVE[j];
label=j;
}
if(label!=0) {
{
a=OBJECTIVE[i];
OBJECTIVE[i]=OBJECTIVE[label];
OBJECTIVE[label]=a;
}
for(j=1; j<=N; j++) {
a=CHROMOSOME[i][j];
CHROMOSOME[i][j]=CHROMOSOME[label][j];
CHROMOSOME[label][j]=a;
}
}
}
}
static void selection()
{
double r, temp[POP_SIZE+1][N+1];
int i, j, k;
for(i=1; i<=POP_SIZE; i++) {
r=myu(0, q[POP_SIZE]);
for(j=0; j<=POP_SIZE; j++) {
if(r<=q[j]) {
for(k=1; k<=N; k++) temp[i][k]=CHROMOSOME[j][k];
break;
}
}
}
for(i=1; i<=POP_SIZE; i++)
for(k=1; k<=N; k++)
CHROMOSOME[i][k]=temp[i][k];
}
static void crossover()
{
int i, j, jj, k, pop;
double a,r;
double x[N+1], y[N+1];
pop=POP_SIZE/2;
for(i=1; i<=pop; i++) {
if(myu(0,1)>P_CROSSOVER) continue;
j=(int)myu(1,16);
jj=(int)myu(1,16);
r=myu(0,1);
for(k=1; k<=15; k++) {
x[k]=int(r*CHROMOSOME[j][k]+(1-r)*CHROMOSOME[jj][k])%(SELECTQUE_SIZE+1);
y[k]=int(r*CHROMOSOME[jj][k]+(1-r)*CHROMOSOME[j][k])%(SELECTQUE_SIZE+1);
}
j=(int)myu(16,26);
jj=(int)myu(16,26);
r=myu(0,1);
for(k=16; k<=26; k++) {
x[k]=int(r*CHROMOSOME[j][k]+(1-r)*CHROMOSOME[jj][k])%(TKQUE_SIZE+1);
y[k]=int(r*CHROMOSOME[jj][k]+(1-r)*CHROMOSOME[j][k])%(SELECTQUE_SIZE+1);
}
j=(int)myu(26,31);
jj=(int)myu(26,31);
r=myu(0,1);
for(k=26; k<=30; k++) {
x[k]=int(r*CHROMOSOME[j][k]+(1-r)*CHROMOSOME[jj][k])%(ZHGQUE_SIZE+1);
y[k]=int(r*CHROMOSOME[jj][k]+(1-r)*CHROMOSOME[j][k])%(ZHGQUE_SIZE+1);
}
a=caucal(x);
if((constraint_check(x)==1)&&a<=(RCH*CHROMOSOME[j][31]+RD*CHROMOSOME[j][32]))
for(k=1; k<=N; k++) CHROMOSOME[j][k]=x[k];
a=caucal(y);
if((constraint_check(y)==1)&&a<=(RCH*CHROMOSOME[jj][31]+RD*CHROMOSOME[jj][32]))
for(k=1; k<=N; k++) CHROMOSOME[jj][k]=y[k];
}
}
static void mutation(void)
{
int i, j, k;
double x[N+1], y[N+1], infty, direction[N+1],a;
double INFTY=10, precision=0.0001;
for(i=1; i<=POP_SIZE; i++) {
if(myu(0,1)>P_MUTATION) continue;
for(k=1; k<=N; k++) x[k] = CHROMOSOME[i][k];
for(k=1; k<=30; k++)
if(myu(0,1)<0.5) direction[k]=myu(-1,1);
else direction[k]=0;
infty=myu(0,INFTY);
while(infty>precision) {
for(j=1; j<=30; j++) y[j]=int(x[j]+infty*direction[j]);
a=caucal(y);
if(constraint_check(y)==1&&a<=(RCH*CHROMOSOME[i][31]+RD*CHROMOSOME[i][32])) {
for(k=1; k<=N; k++) CHROMOSOME[i][k]=y[k];
break;
}
infty=myu(0,infty);
}
}
}
Loading…
Cancel
Save