@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.exception;
|
||||
|
||||
public class ActionFailureException extends RuntimeException {
|
||||
private static final long serialVersionUID = 4425249765923293627L;
|
||||
|
||||
public ActionFailureException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ActionFailureException(String paramString) {
|
||||
super(paramString);
|
||||
}
|
||||
|
||||
public ActionFailureException(String paramString, Throwable paramThrowable) {
|
||||
super(paramString, paramThrowable);
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.remote;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.ui.NotesListActivity;
|
||||
import net.micode.notes.ui.NotesPreferenceActivity;
|
||||
|
||||
|
||||
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
|
||||
|
||||
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;
|
||||
|
||||
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);
|
||||
mTaskManager = GTaskManager.getInstance();
|
||||
}
|
||||
|
||||
public void cancelSync() {
|
||||
mTaskManager.cancelSync();
|
||||
}
|
||||
|
||||
public void publishProgess(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.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);
|
||||
|
||||
} else {
|
||||
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
|
||||
NotesListActivity.class), 0);
|
||||
}
|
||||
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)));
|
||||
return mTaskManager.sync(mContext, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(String... progress) {
|
||||
showNotification(R.string.ticker_syncing, progress[0]);
|
||||
if (mContext instanceof GTaskSyncService) {
|
||||
((GTaskSyncService) mContext).sendBroadcast(progress[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
if (result == GTaskManager.STATE_SUCCESS) {
|
||||
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));
|
||||
}
|
||||
if (mOnCompleteListener != null) {
|
||||
new Thread(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
mOnCompleteListener.onComplete();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,585 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.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;
|
||||
|
||||
|
||||
public class GTaskClient {
|
||||
private static final String TAG = GTaskClient.class.getSimpleName();
|
||||
|
||||
private static final String GTASK_URL = "https://mail.google.com/tasks/";
|
||||
|
||||
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
|
||||
|
||||
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
|
||||
|
||||
private static GTaskClient mInstance = null;
|
||||
|
||||
private DefaultHttpClient mHttpClient;
|
||||
|
||||
private String mGetUrl;
|
||||
|
||||
private String mPostUrl;
|
||||
|
||||
private long mClientVersion;
|
||||
|
||||
private boolean mLoggedin;
|
||||
|
||||
private long mLastLoginTime;
|
||||
|
||||
private int mActionId;
|
||||
|
||||
private Account mAccount;
|
||||
|
||||
private JSONArray mUpdateArray;
|
||||
|
||||
private GTaskClient() {
|
||||
mHttpClient = null;
|
||||
mGetUrl = GTASK_GET_URL;
|
||||
mPostUrl = GTASK_POST_URL;
|
||||
mClientVersion = -1;
|
||||
mLoggedin = false;
|
||||
mLastLoginTime = 0;
|
||||
mActionId = 1;
|
||||
mAccount = null;
|
||||
mUpdateArray = null;
|
||||
}
|
||||
|
||||
public static synchronized GTaskClient getInstance() {
|
||||
if (mInstance == null) {
|
||||
mInstance = new GTaskClient();
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private int getActionId() {
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,800 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.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;
|
||||
|
||||
|
||||
public class GTaskManager {
|
||||
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;
|
||||
|
||||
private Activity mActivity;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
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;
|
||||
|
||||
private HashMap<String, Long> mGidToNid;
|
||||
|
||||
private HashMap<Long, String> mNidToGid;
|
||||
|
||||
private 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() {
|
||||
if (mInstance == null) {
|
||||
mInstance = new GTaskManager();
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public synchronized void setActivityContext(Activity activity) {
|
||||
// used for getting authtoken
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getSyncAccount() {
|
||||
return GTaskClient.getInstance().getSyncAccount().name;
|
||||
}
|
||||
|
||||
public void cancelSync() {
|
||||
mCancelled = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.remote;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class GTaskSyncService extends 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";
|
||||
|
||||
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() {
|
||||
mSyncTask = null;
|
||||
sendBroadcast("");
|
||||
stopSelf();
|
||||
}
|
||||
});
|
||||
sendBroadcast("");
|
||||
mSyncTask.execute();
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelSync() {
|
||||
if (mSyncTask != null) {
|
||||
mSyncTask.cancelSync();
|
||||
}
|
||||
}
|
||||
|
||||
@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)) {
|
||||
case ACTION_START_SYNC:
|
||||
startSync();
|
||||
break;
|
||||
case ACTION_CANCEL_SYNC:
|
||||
cancelSync();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
if (mSyncTask != null) {
|
||||
mSyncTask.cancelSync();
|
||||
}
|
||||
}
|
||||
|
||||
public IBinder onBind(Intent intent) {
|
||||
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);
|
||||
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
|
||||
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);
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.data;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.tool.GTaskStringUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
public class MetaData extends Task {
|
||||
private final static String TAG = MetaData.class.getSimpleName();
|
||||
|
||||
private String mRelatedGid = null;
|
||||
|
||||
public void setMeta(String gid, JSONObject metaInfo) {
|
||||
try {
|
||||
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "failed to put related gid");
|
||||
}
|
||||
setNotes(metaInfo.toString());
|
||||
setName(GTaskStringUtils.META_NOTE_NAME);
|
||||
}
|
||||
|
||||
public String getRelatedGid() {
|
||||
return mRelatedGid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWorthSaving() {
|
||||
return getNotes() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentByRemoteJSON(JSONObject js) {
|
||||
super.setContentByRemoteJSON(js);
|
||||
if (getNotes() != null) {
|
||||
try {
|
||||
JSONObject metaInfo = new JSONObject(getNotes().trim());
|
||||
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
|
||||
} catch (JSONException e) {
|
||||
Log.w(TAG, "failed to get related gid");
|
||||
mRelatedGid = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.exception;
|
||||
|
||||
public class NetworkFailureException extends Exception {
|
||||
private static final long serialVersionUID = 2107610287180234136L;
|
||||
|
||||
public NetworkFailureException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NetworkFailureException(String paramString) {
|
||||
super(paramString);
|
||||
}
|
||||
|
||||
public NetworkFailureException(String paramString, Throwable paramThrowable) {
|
||||
super(paramString, paramThrowable);
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.data;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public abstract class Node {
|
||||
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;
|
||||
|
||||
private String mName;
|
||||
|
||||
private long mLastModified;
|
||||
|
||||
private boolean mDeleted;
|
||||
|
||||
public Node() {
|
||||
mGid = null;
|
||||
mName = "";
|
||||
mLastModified = 0;
|
||||
mDeleted = false;
|
||||
}
|
||||
|
||||
public abstract JSONObject getCreateAction(int actionId);
|
||||
|
||||
public abstract JSONObject getUpdateAction(int actionId);
|
||||
|
||||
public abstract void setContentByRemoteJSON(JSONObject js);
|
||||
|
||||
public abstract void setContentByLocalJSON(JSONObject js);
|
||||
|
||||
public abstract JSONObject getLocalJSONFromContent();
|
||||
|
||||
public abstract int getSyncAction(Cursor c);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public String getGid() {
|
||||
return this.mGid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.mName;
|
||||
}
|
||||
|
||||
public long getLastModified() {
|
||||
return this.mLastModified;
|
||||
}
|
||||
|
||||
public boolean getDeleted() {
|
||||
return this.mDeleted;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.data;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.DataColumns;
|
||||
import net.micode.notes.data.Notes.DataConstants;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
|
||||
import net.micode.notes.gtask.exception.ActionFailureException;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
public class SqlData {
|
||||
private static final String TAG = SqlData.class.getSimpleName();
|
||||
|
||||
private static final int INVALID_ID = -99999;
|
||||
|
||||
public static final String[] PROJECTION_DATA = new String[] {
|
||||
DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1,
|
||||
DataColumns.DATA3
|
||||
};
|
||||
|
||||
public static final int DATA_ID_COLUMN = 0;
|
||||
|
||||
public static final int DATA_MIME_TYPE_COLUMN = 1;
|
||||
|
||||
public static final int DATA_CONTENT_COLUMN = 2;
|
||||
|
||||
public static final int DATA_CONTENT_DATA_1_COLUMN = 3;
|
||||
|
||||
public static final int DATA_CONTENT_DATA_3_COLUMN = 4;
|
||||
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
private boolean mIsCreate;
|
||||
|
||||
private long mDataId;
|
||||
|
||||
private String mDataMimeType;
|
||||
|
||||
private String mDataContent;
|
||||
|
||||
private long mDataContentData1;
|
||||
|
||||
private String mDataContentData3;
|
||||
|
||||
private ContentValues mDiffDataValues;
|
||||
|
||||
public SqlData(Context context) {
|
||||
mContentResolver = context.getContentResolver();
|
||||
mIsCreate = true;
|
||||
mDataId = INVALID_ID;
|
||||
mDataMimeType = DataConstants.NOTE;
|
||||
mDataContent = "";
|
||||
mDataContentData1 = 0;
|
||||
mDataContentData3 = "";
|
||||
mDiffDataValues = new ContentValues();
|
||||
}
|
||||
|
||||
public SqlData(Context context, Cursor c) {
|
||||
mContentResolver = context.getContentResolver();
|
||||
mIsCreate = false;
|
||||
loadFromCursor(c);
|
||||
mDiffDataValues = new ContentValues();
|
||||
}
|
||||
|
||||
private void loadFromCursor(Cursor c) {
|
||||
mDataId = c.getLong(DATA_ID_COLUMN);
|
||||
mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);
|
||||
mDataContent = c.getString(DATA_CONTENT_COLUMN);
|
||||
mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN);
|
||||
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
|
||||
}
|
||||
|
||||
public void setContent(JSONObject js) throws JSONException {
|
||||
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
|
||||
if (mIsCreate || mDataId != dataId) {
|
||||
mDiffDataValues.put(DataColumns.ID, dataId);
|
||||
}
|
||||
mDataId = dataId;
|
||||
|
||||
String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE)
|
||||
: DataConstants.NOTE;
|
||||
if (mIsCreate || !mDataMimeType.equals(dataMimeType)) {
|
||||
mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType);
|
||||
}
|
||||
mDataMimeType = dataMimeType;
|
||||
|
||||
String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : "";
|
||||
if (mIsCreate || !mDataContent.equals(dataContent)) {
|
||||
mDiffDataValues.put(DataColumns.CONTENT, dataContent);
|
||||
}
|
||||
mDataContent = dataContent;
|
||||
|
||||
long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0;
|
||||
if (mIsCreate || mDataContentData1 != dataContentData1) {
|
||||
mDiffDataValues.put(DataColumns.DATA1, dataContentData1);
|
||||
}
|
||||
mDataContentData1 = dataContentData1;
|
||||
|
||||
String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : "";
|
||||
if (mIsCreate || !mDataContentData3.equals(dataContentData3)) {
|
||||
mDiffDataValues.put(DataColumns.DATA3, dataContentData3);
|
||||
}
|
||||
mDataContentData3 = dataContentData3;
|
||||
}
|
||||
|
||||
public JSONObject getContent() throws JSONException {
|
||||
if (mIsCreate) {
|
||||
Log.e(TAG, "it seems that we haven't created this in database yet");
|
||||
return null;
|
||||
}
|
||||
JSONObject js = new JSONObject();
|
||||
js.put(DataColumns.ID, mDataId);
|
||||
js.put(DataColumns.MIME_TYPE, mDataMimeType);
|
||||
js.put(DataColumns.CONTENT, mDataContent);
|
||||
js.put(DataColumns.DATA1, mDataContentData1);
|
||||
js.put(DataColumns.DATA3, mDataContentData3);
|
||||
return js;
|
||||
}
|
||||
|
||||
public void commit(long noteId, boolean validateVersion, long version) {
|
||||
|
||||
if (mIsCreate) {
|
||||
if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {
|
||||
mDiffDataValues.remove(DataColumns.ID);
|
||||
}
|
||||
|
||||
mDiffDataValues.put(DataColumns.NOTE_ID, noteId);
|
||||
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);
|
||||
try {
|
||||
mDataId = Long.valueOf(uri.getPathSegments().get(1));
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Get note id error :" + e.toString());
|
||||
throw new ActionFailureException("create note failed");
|
||||
}
|
||||
} else {
|
||||
if (mDiffDataValues.size() > 0) {
|
||||
int result = 0;
|
||||
if (!validateVersion) {
|
||||
result = mContentResolver.update(ContentUris.withAppendedId(
|
||||
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);
|
||||
} else {
|
||||
result = mContentResolver.update(ContentUris.withAppendedId(
|
||||
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues,
|
||||
" ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE
|
||||
+ " WHERE " + NoteColumns.VERSION + "=?)", new String[] {
|
||||
String.valueOf(noteId), String.valueOf(version)
|
||||
});
|
||||
}
|
||||
if (result == 0) {
|
||||
Log.w(TAG, "there is no update. maybe user updates note when syncing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mDiffDataValues.clear();
|
||||
mIsCreate = false;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return mDataId;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:color="#88555555" />
|
||||
<item android:state_selected="true" android:color="#ff999999" />
|
||||
<item android:color="#ff000000" />
|
||||
</selector>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#50000000" />
|
||||
</selector>
|
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 245 B |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 443 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 554 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.1 KiB |
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true"
|
||||
android:drawable="@drawable/new_note_pressed" />
|
||||
<item
|
||||
android:drawable="@drawable/new_note_normal" />
|
||||
</selector>
|
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/account_dialog_title"
|
||||
style="?android:attr/textAppearanceMedium"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="-2.7dip"
|
||||
android:layout_marginBottom="-2.7dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/account_dialog_subtitle"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dip"
|
||||
android:layout_marginBottom="1dip"
|
||||
android:gravity="center"/>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="50dip"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/preferences_add_account" />
|
||||
</LinearLayout>
|
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/date"
|
||||
android:layout_width="120dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
/>
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/hour"
|
||||
android:layout_width="50dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
/>
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/minute"
|
||||
android:layout_width="50dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
/>
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/amPm"
|
||||
android:layout_width="50dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
/>
|
||||
</LinearLayout>
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<EditText
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/et_foler_name"
|
||||
android:layout_width="fill_parent"
|
||||
android:hint="@string/hint_foler_name"
|
||||
android:layout_height="fill_parent" />
|
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:minHeight="50dip" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_folder_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearancePrimaryItem" />
|
||||
</LinearLayout>
|
@ -0,0 +1,400 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/list_background"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/note_title"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_modified_date"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="left|center_vertical"
|
||||
android:layout_marginRight="8dip"
|
||||
android:textAppearance="@style/TextAppearanceSecondaryItem" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_alert_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@drawable/title_alert" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_alert_date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="2dip"
|
||||
android:layout_marginRight="8dip"
|
||||
android:textAppearance="@style/TextAppearanceSecondaryItem" />
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/bg_btn_set_color" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/sv_note_edit"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="7dip"
|
||||
android:background="@drawable/bg_color_btn_mask" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:layout_gravity="left|top"
|
||||
android:fadingEdgeLength="0dip">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<net.micode.notes.ui.NoteEditText
|
||||
android:id="@+id/note_edit_view"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="left|top"
|
||||
android:background="@null"
|
||||
android:autoLink="all"
|
||||
android:linksClickable="false"
|
||||
android:minLines="12"
|
||||
android:textAppearance="@style/TextAppearancePrimaryItem"
|
||||
android:lineSpacingMultiplier="1.2" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/note_edit_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginLeft="-10dip"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="7dip"
|
||||
android:background="@drawable/bg_color_btn_mask" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_set_bg_color"
|
||||
android:layout_height="43dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:background="@drawable/bg_color_btn_mask"
|
||||
android:layout_gravity="top|right" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/note_bg_color_selector"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/note_edit_color_selector_panel"
|
||||
android:layout_marginTop="30dip"
|
||||
android:layout_marginRight="8dip"
|
||||
android:layout_gravity="top|right"
|
||||
android:visibility="gone">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_yellow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_yellow_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:layout_marginRight="5dip"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_blue"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_blue_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:layout_marginRight="3dip"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_white"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_white_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:layout_marginRight="2dip"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_green"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_green_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_red"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg_red_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/font_size_selector"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/font_size_selector_bg"
|
||||
android:layout_gravity="bottom"
|
||||
android:visibility="gone">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/ll_font_small"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/font_small"
|
||||
android:layout_marginBottom="5dip" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/menu_font_small"
|
||||
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_small_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginBottom="-7dip"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/ll_font_normal"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/font_normal"
|
||||
android:layout_marginBottom="5dip" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/menu_font_normal"
|
||||
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_medium_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginBottom="-7dip"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/ll_font_large"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/font_large"
|
||||
android:layout_marginBottom="5dip" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/menu_font_large"
|
||||
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_large_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginBottom="-7dip"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/ll_font_super"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/font_super"
|
||||
android:layout_marginBottom="5dip" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/menu_font_super"
|
||||
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_super_select"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:focusable="false"
|
||||
android:visibility="gone"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginBottom="-7dip"
|
||||
android:src="@drawable/selected" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cb_edit_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="28dip"
|
||||
android:checked="false"
|
||||
android:focusable="false"
|
||||
android:layout_gravity="top|left" />
|
||||
|
||||
<net.micode.notes.ui.NoteEditText
|
||||
android:id="@+id/et_edit_text"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAppearance="@style/TextAppearancePrimaryItem"
|
||||
android:background="@null" />
|
||||
</LinearLayout>
|
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/note_item"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearancePrimaryItem"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearanceSecondaryItem" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@android:id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:clickable="false"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_alert_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|right"/>
|
||||
</FrameLayout>
|
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/list_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title_bar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/title_bar_bg"
|
||||
android:visibility="gone"
|
||||
android:gravity="center_vertical"
|
||||
android:singleLine="true"
|
||||
android:textColor="#FFEAD1AE"
|
||||
android:textSize="@dimen/text_font_size_medium" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/notes_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
android:cacheColorHint="@null"
|
||||
android:listSelector="@android:color/transparent"
|
||||
android:divider="@null"
|
||||
android:fadingEdge="@null" />
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_new_note"
|
||||
android:background="@drawable/new_note"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:layout_gravity="bottom" />
|
||||
</FrameLayout>
|
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/navigation_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button android:id="@+id/selection_menu"
|
||||
android:divider="?android:attr/listDividerAlertDialog"
|
||||
android:singleLine="true"
|
||||
android:gravity="left|center_vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
style="?android:attr/borderlessButtonStyle" />
|
||||
</LinearLayout>
|
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<View
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="100dip"
|
||||
android:visibility="invisible"
|
||||
android:focusable="false"
|
||||
android:background="@drawable/list_footer_bg" />
|
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<Button
|
||||
android:id="@+id/preference_sync_button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dip"
|
||||
android:layout_marginLeft="30dip"
|
||||
android:layout_marginRight="30dip"
|
||||
style="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/preferences_button_sync_immediately"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/prefenerece_sync_status_textview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="146dip"
|
||||
android:layout_height="146dip">
|
||||
<ImageView
|
||||
android:id="@+id/widget_bg_image"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
<TextView
|
||||
android:id="@+id/widget_text"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:paddingTop="20dip"
|
||||
android:paddingLeft="15dip"
|
||||
android:paddingRight="15dip"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#FF663300"
|
||||
android:maxLines="6"
|
||||
android:lineSpacingMultiplier="1.2" />
|
||||
</FrameLayout>
|
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="294dip"
|
||||
android:layout_height="294dip">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/widget_bg_image"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_text"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:paddingTop="40dip"
|
||||
android:paddingLeft="15dip"
|
||||
android:paddingRight="15dip"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#FF663300"
|
||||
android:maxLines="12"
|
||||
android:lineSpacingMultiplier="1.2" />
|
||||
</FrameLayout>
|
@ -1,9 +0,0 @@
|
||||
# generated files
|
||||
bin/
|
||||
gen/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
project.properties
|
||||
.settings/
|
||||
.classpath
|
||||
.project
|
@ -1,150 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="net.micode.notes"
|
||||
android:versionCode="1"
|
||||
android:versionName="0.1" >
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" />
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:icon="@drawable/icon_app"
|
||||
android:label="@string/app_name" >
|
||||
<activity
|
||||
android:name=".ui.NotesListActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/NoteTheme"
|
||||
android:uiOptions="splitActionBarWhenNarrow"
|
||||
android:windowSoftInputMode="adjustPan" >
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.NoteEditActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/NoteTheme" >
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="vnd.android.cursor.item/text_note" />
|
||||
<data android:mimeType="vnd.android.cursor.item/call_note" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.INSERT_OR_EDIT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="vnd.android.cursor.item/text_note" />
|
||||
<data android:mimeType="vnd.android.cursor.item/call_note" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="net.micode.notes.data.NotesProvider"
|
||||
android:authorities="micode_notes"
|
||||
android:multiprocess="true" />
|
||||
|
||||
<receiver
|
||||
android:name=".widget.NoteWidgetProvider_2x"
|
||||
android:label="@string/app_widget2x2" >
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
|
||||
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_2x_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".widget.NoteWidgetProvider_4x"
|
||||
android:label="@string/app_widget4x4" >
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
|
||||
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_4x_info" />
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".ui.AlarmInitReceiver" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name="net.micode.notes.ui.AlarmReceiver"
|
||||
android:process=":remote" >
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".ui.AlarmAlertActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="net.micode.notes.ui.NotesPreferenceActivity"
|
||||
android:label="@string/preferences_title"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@android:style/Theme.Holo.Light" >
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name="net.micode.notes.gtask.remote.GTaskSyncService"
|
||||
android:exported="false" >
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:value=".ui.NoteEditActivity" />
|
||||
</application>
|
||||
</manifest>
|
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_delete"
|
||||
android:title="@string/menu_delete" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_font_size"
|
||||
android:title="@string/menu_font_size"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_list_mode"
|
||||
android:title="@string/menu_list_mode" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_share"
|
||||
android:title="@string/menu_share"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_send_to_desktop"
|
||||
android:title="@string/menu_send_to_desktop"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_alert"
|
||||
android:title="@string/menu_alert" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_delete_remind"
|
||||
android:title="@string/menu_remove_remind" />
|
||||
</menu>
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/menu_search"
|
||||
android:title="@string/menu_search" />
|
||||
</menu>
|