public class GetWebpageTitleTask extends AsyncTask<String, Integer, String>{
	private String url;
	
	@Override
	protected String doInBackground(String... args) {
		
		if(args.length > 0 && args[0] != null && args[0] != "") {
    		url = args[0];
	
    		return NetworkUtilities.getWebpageTitle(url);
		} else return "";
		
	}
} public abstract class FragmentBaseActivity extends FragmentActivity {
	
	protected AccountManager mAccountManager;
	public Account mAccount;
	public Context mContext;
	public String username = null;
	protected SharedPreferences settings;
	
	public boolean privateDefault;
	public String defaultAction;
	public boolean markAsRead;
	public String readingBackground;
	public String readingFont;
	public String readingMargins;
	public String readingFontSize;
	public String readingLineSpace;
	
	private boolean first = true;
	
	Bundle savedState;
	
	@Override
	@TargetApi(14)
	public void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		
		mContext = this;
		mAccountManager = AccountManager.get(this);
		settings = PreferenceManager.getDefaultSharedPreferences(this);
		
		loadSettings();
		init();
		
		if(android.os.Build.VERSION.SDK_INT >= 14) {
			getActionBar().setHomeButtonEnabled(true);
		}

		Intent intent = getIntent();
		
		if(Intent.ACTION_SEARCH.equals(intent.getAction()) && !intent.hasExtra("MainSearchResults")){
			if(intent.hasExtra(SearchManager.QUERY)){
				Intent i = new Intent(this, MainSearchResults.class);
				i.putExtras(intent.getExtras());
				startActivity(i);
				finish();
			} else {
				onSearchRequested();
			}
		} else if(Constants.ACTION_SEARCH_SUGGESTION.equals(intent.getAction())) {
			Uri data = intent.getData();
			String path = null;
			String tagname = null;
			
			if(data != null) {
				path = data.getPath();
				tagname = data.getQueryParameter("tagname");
			}
			
			if(data.getScheme() == null || !data.getScheme().equals("content")){
				Intent i = new Intent(Intent.ACTION_VIEW, data);
				startActivity(i);
				finish();				
			} else if(path.contains("bookmarks") && TextUtils.isDigitsOnly(data.getLastPathSegment()) && intent.hasExtra(SearchManager.USER_QUERY)) {
				Intent viewBookmark = new Intent(this, ViewBookmark.class);
				viewBookmark.setAction(Intent.ACTION_VIEW);
				viewBookmark.setData(data);
				viewBookmark.removeExtra(SearchManager.USER_QUERY);
				Log.d("View Bookmark Uri", data.toString());
				startActivity(viewBookmark);
				finish();
			} else if(tagname != null) {
				Intent viewTags = new Intent(this, BrowseBookmarks.class);
				viewTags.setData(data);
				
				Log.d("View Tags Uri", data.toString());
				startActivity(viewTags);
				finish();
			}
		}
	}
	
	private void init(){
		
		if(mAccountManager.getAccountsByType(Constants.ACCOUNT_TYPE).length < 1) {		
			Intent i = new Intent(this, AuthenticatorActivity.class);
			startActivityForResult(i, 0);
			
			return;
		} else {
			if(mAccount == null || username == null)
				loadAccounts();
		}
	}
	
	public void searchHandler(View v) {
		onSearchRequested();
	}
	
	@Override
	public void onResume(){
		super.onResume();
		
		if(!first) {
			loadSettings();
			init();
		}
		first = false;
	}
	
	private void loadAccounts(){
		if(mAccountManager.getAccountsByType(Constants.ACCOUNT_TYPE).length > 0) {	
			mAccount = mAccountManager.getAccountsByType(Constants.ACCOUNT_TYPE)[0];
		}
		
		username = mAccount.name;
	}
	
	@TargetApi(11)
	public void setupSearch(Menu menu){
	    if(android.os.Build.VERSION.SDK_INT >= 11) {
	    	SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
	    	SearchView searchView = (SearchView)menu.findItem(R.id.menu_search).getActionView();
	    	searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
	    	searchView.setSubmitButtonEnabled(false);
	    }
	}
	
	private void loadSettings(){
    	privateDefault = settings.getBoolean("pref_save_private_default", false);
    	defaultAction = settings.getString("pref_view_bookmark_default_action", "browser");
    	markAsRead = settings.getBoolean("pref_markasread", false);
    	readingBackground = settings.getString("pref_reading_background", "-1");
    	readingFont = settings.getString("pref_reading_font", "Roboto-Regular");
    	readingMargins = settings.getString("pref_reading_margins", "20");
    	readingFontSize = settings.getString("pref_reading_fontsize", "16");
    	readingLineSpace = settings.getString("pref_reading_linespace", "5");
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
	    MenuInflater inflater = getMenuInflater();
	    inflater.inflate(R.menu.base_menu, menu);
	    
	    return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    switch (item.getItemId()) {
	        case android.R.id.home:
	            Intent intent = new Intent(this, Main.class);
	            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
	            startActivity(intent);
	            return true;
		    case R.id.menu_addbookmark:
				startActivity(IntentHelper.AddBookmark(null, mAccount.name, this));
				return true;
		    case R.id.menu_search:
		    	onSearchRequested();
		    	return true;
		    case R.id.menu_settings:
				Intent prefs = new Intent(this, Preferences.class);
				startActivity(prefs);
		        return true;
		    default:
		        return super.onOptionsItemSelected(item);
	    }
	}
	
	public boolean isMyself() {
		if(mAccount != null && username != null)
			return mAccount.name.equals(username);
		else return false;
	}
	
	@Override
	public void setTitle(CharSequence title){
		super.setTitle(title);

		if(this.findViewById(R.id.action_bar_title) != null) {
			((TextView)this.findViewById(R.id.action_bar_title)).setText(title);
		}
	}
	
	protected void onActivityResult(int requestCode, int resultCode, Intent data){	
		if(resultCode == Activity.RESULT_CANCELED){
			finish();
		}
	}
} public class DeliciousApi {
	
    private static final String TAG = "DeliciousApi";

    public static final String USER_AGENT = "AuthenticationService/1.0";
    public static final int REGISTRATION_TIMEOUT = 30 * 1000; // ms

    public static final String FETCH_TAGS_URI = "v1/tags/get";
    public static final String FETCH_BUNDLES_URI = "v1/tags/bundles/all";
    public static final String FETCH_SUGGESTED_TAGS_URI = "v1/posts/suggest";
    public static final String FETCH_BOOKMARKS_URI = "v1/posts/all";
    public static final String FETCH_CHANGED_BOOKMARKS_URI = "v1/posts/all";
    public static final String FETCH_BOOKMARK_URI = "v1/posts/get";
    public static final String LAST_UPDATE_URI = "v1/posts/update";
    public static final String DELETE_BOOKMARK_URI = "v1/posts/delete";
    public static final String ADD_BOOKMARKS_URI = "v1/posts/add";
  
    private static final String SCHEME = "http";
    private static final String SCHEME_HTTP = "http";
    private static final String DELICIOUS_AUTHORITY = "api.del.icio.us";
    private static final int PORT = 80;
 
    private static final AuthScope SCOPE = new AuthScope(DELICIOUS_AUTHORITY, PORT);

    /**
     * Gets timestamp of last update to data on Delicious servers.
     * 
     * @param account The account being synced.
     * @param context The current application context.
     * @return An Update object containing the timestamp and the number of new bookmarks in the
     * inbox.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    public static Update lastUpdate(Account account, Context context)
    	throws IOException, AuthenticationException, TooManyRequestsException {

    	String response = null;
    	InputStream responseStream = null;
    	TreeMap<String, String> params = new TreeMap<String, String>();
    	Update update = null;
    	
    	responseStream = DeliciousApiCall(LAST_UPDATE_URI, params, account, context);
    	response = convertStreamToString(responseStream);
    	responseStream.close();
    	
    	try{
	        if (response.contains("<?xml")) {
	        	update = Update.valueOf(response);
	        } else {
	            Log.e(TAG, "Server error in fetching bookmark list");
	            throw new IOException();
	        }
    	}
    	catch(Exception e) {
    		Log.e(TAG, "Server error in fetching bookmark list");
    		throw new IOException();
    	}
        return update;
    }
    
    /**
     * Sends a request to Delicious's Add Bookmark api.
     * 
     * @param bookmark The bookmark to be added.
     * @param account The account being synced.
     * @param context The current application context.
     * @return A boolean indicating whether or not the api call was successful.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     * @throws TokenRejectedException If the oauth token is reported to be expired.
     * @throws Exception If an unknown error is encountered.
     */
    public static Boolean addBookmark(Bookmark bookmark, Account account, Context context) 
    	throws IOException, AuthenticationException, TooManyRequestsException {

    	String url = bookmark.getUrl();
    	if(url.endsWith("/")) {
    		url = url.substring(0, url.lastIndexOf('/'));
    	}
    	
    	TreeMap<String, String> params = new TreeMap<String, String>();
    	  	
		params.put("description", bookmark.getDescription());
		params.put("extended", bookmark.getNotes());
		
		// until delicious fixes their api we need to use commas to delimit tags
		// tags with spaces, although supported by the web interface, will not work with deliciousdroid
		params.put("tags", bookmark.getTagString().replace(' ', ','));
		params.put("url", bookmark.getUrl());
		params.put("replace", "yes");
		
		if(bookmark.getShared()){
			params.put("shared", "yes");
		} else params.put("shared", "no");
		
		String uri = ADD_BOOKMARKS_URI;
		String response = null;
		InputStream responseStream = null;

    	responseStream = DeliciousApiCall(uri, params, account, context);
    	response = convertStreamToString(responseStream);
    	responseStream.close();

        if (response.contains("<result code=\"done\"/>")) {
            return true;
        } else {
        	Log.e(TAG, "Server error in adding bookmark");
            throw new IOException();
        }
    }
    
    /**
     * Sends a request to Delicious's Delete Bookmark api.
     * 
     * @param bookmark The bookmark to be deleted.
     * @param account The account being synced.
     * @param context The current application context.
     * @return A boolean indicating whether or not the api call was successful.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    public static Boolean deleteBookmark(Bookmark bookmark, Account account, Context context) 
    	throws IOException, AuthenticationException, TooManyRequestsException {

    	TreeMap<String, String> params = new TreeMap<String, String>();
    	String response = null;
    	InputStream responseStream = null;
    	String url = DELETE_BOOKMARK_URI;

    	params.put("url", bookmark.getUrl());

    	responseStream = DeliciousApiCall(url, params, account, context);
    	response = convertStreamToString(responseStream);
    	responseStream.close();
    	
        if (response.contains("<result code=\"done\"") || response.contains("<result code=\"item not found\"")) {
            return true;
        } else {
            Log.e(TAG, "Server error in fetching bookmark list");
            throw new IOException();
        }
    }
    
    /**
     * Retrieves a specific list of bookmarks from Delicious.
     * 
     * @param hashes A list of bookmark hashes to be retrieved.  
     * 	The hashes are MD5 hashes of the URL of the bookmark.
     * 
     * @param account The account being synced.
     * @param context The current application context.
     * @return A list of bookmarks received from the server.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    public static ArrayList<Bookmark> getBookmark(ArrayList<String> hashes, Account account,
        Context context) throws IOException, AuthenticationException, TooManyRequestsException {

    	ArrayList<Bookmark> bookmarkList = new ArrayList<Bookmark>();
    	TreeMap<String, String> params = new TreeMap<String, String>();
    	String hashString = "";
    	InputStream responseStream = null;
    	String url = FETCH_BOOKMARK_URI;

    	for(String h : hashes){
    		if(hashes.get(0) != h){
    			hashString += "+";
    		}
    		hashString += h;
    	}
    	params.put("meta", "yes");
    	params.put("hashes", hashString);

    	responseStream = DeliciousApiCall(url, params, account, context);
    	SaxBookmarkParser parser = new SaxBookmarkParser(responseStream);
    	
    	try {
			bookmarkList = parser.parse();
		} catch (ParseException e) {
            Log.e(TAG, "Server error in fetching bookmark list");
            throw new IOException();
		}

        responseStream.close();
        return bookmarkList;
    }
    
    /**
     * Retrieves the entire list of bookmarks for a user from Pinboard.
     * 
     * @param tagname If specified, will only retrieve bookmarks with a specific tag.
     * @param account The account being synced.
     * @param context The current application context.
     * @return A list of bookmarks received from the server.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     * @throws TooManyRequestsException 
     */
    public static ArrayList<Bookmark> getAllBookmarks(String tagName, Account account, Context context) 
    	throws IOException, AuthenticationException, TooManyRequestsException {

        return getAllBookmarks(tagName, 0, 0, account, context);
    }
        
    /**
     * Retrieves the entire list of bookmarks for a user from Delicious.  Warning:  Overuse of this 
     * api call will get your account throttled.
     * 
     * @param tagname If specified, will only retrieve bookmarks with a specific tag.
     * @param account The account being synced.
     * @param context The current application context.
     * @return A list of bookmarks received from the server.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    public static ArrayList<Bookmark> getAllBookmarks(String tagName, int start, int count, Account account, Context context) 
	throws IOException, AuthenticationException, TooManyRequestsException {
    	ArrayList<Bookmark> bookmarkList = new ArrayList<Bookmark>();

    	InputStream responseStream = null;
    	TreeMap<String, String> params = new TreeMap<String, String>();
    	String url = FETCH_BOOKMARKS_URI;

    	if(tagName != null && tagName != ""){
    		params.put("tag", tagName);
    	}
    	
    	if(start != 0){
    		params.put("start", Integer.toString(start));
    	}
    	
    	if(count != 0){
    		params.put("results", Integer.toString(count));
    	}
    	
    	params.put("meta", "yes");

    	responseStream = DeliciousApiCall(url, params, account, context);
    	SaxBookmarkParser parser = new SaxBookmarkParser(responseStream);
    	
    	//Log.d("kdf", convertStreamToString(responseStream));
    	
    	try {
			bookmarkList = parser.parse();
		} catch (ParseException e) {
            Log.e(TAG, "Server error in fetching bookmark list");
            throw new IOException();
		}

        responseStream.close();
        
        return bookmarkList;
    }
    
    /**
     * Retrieves a list of all bookmarks, with only their URL hash and a change (meta) hash,
     * to determine what bookmarks have changed since the last update.
     * 
     * @param account The account being synced.
     * @param context The current application context.
     * @return A list of bookmarks received from the server with only the URL hash and meta hash.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    public static ArrayList<Bookmark> getChangedBookmarks(Account account, Context context) 
    	throws IOException, AuthenticationException {
    	
    	ArrayList<Bookmark> bookmarkList = new ArrayList<Bookmark>();
    	InputStream responseStream = null;
    	TreeMap<String, String> params = new TreeMap<String, String>();
    	String url = FETCH_CHANGED_BOOKMARKS_URI;

    	params.put("hashes", "yes");

    	responseStream = DeliciousApiCall(url, params, account, context);
    	SaxBookmarkParser parser = new SaxBookmarkParser(responseStream);

        try {
        	bookmarkList = parser.parse();
        } catch (ParseException e) {
        	Log.e(TAG, "Server error in fetching bookmark list");
        	throw new IOException();
        }

        responseStream.close();
        return bookmarkList;
    }
    
    /**
     * Retrieves a list of suggested tags for a URL.
     * 
     * @param suggestUrl The URL to get suggested tags for.
     * @param account The account being synced.
     * @param context The current application context.
     * @return A list of tags suggested for the provided url.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    public static ArrayList<Tag> getSuggestedTags(String suggestUrl, Account account, Context context) 
    	throws IOException, AuthenticationException, TooManyRequestsException {
    	
    	ArrayList<Tag> tagList = new ArrayList<Tag>();
    	
		if(!suggestUrl.startsWith("http")){
			suggestUrl = "http://" + suggestUrl;
		}

    	InputStream responseStream = null;
    	TreeMap<String, String> params = new TreeMap<String, String>();
    	params.put("url", suggestUrl);
    	
    	String url = FETCH_SUGGESTED_TAGS_URI;
    	  	
    	responseStream = DeliciousApiCall(url, params, account, context);
    	SaxTagParser parser = new SaxTagParser(responseStream);
    	
    	try {
			tagList = parser.parseSuggested();
		} catch (ParseException e) {
            Log.e(TAG, "Server error in fetching bookmark list");
            throw new IOException();
		}

        responseStream.close();
        return tagList;
    }
    
    /**
     * Retrieves a list of all tags for a user from Delicious.
     * 
     * @param account The account being synced.
     * @param context The current application context.
     * @return A list of the users tags.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    public static ArrayList<Tag> getTags(Account account, Context context) 
    	throws IOException, AuthenticationException, TooManyRequestsException {
    	
    	ArrayList<Tag> tagList = new ArrayList<Tag>();

    	InputStream responseStream = null;
    	final TreeMap<String, String> params = new TreeMap<String, String>();
    	  	
    	responseStream = DeliciousApiCall(FETCH_TAGS_URI, params, account, context);
    	final SaxTagParser parser = new SaxTagParser(responseStream);
    	
    	try {
			tagList = parser.parse();
		} catch (ParseException e) {
            Log.e(TAG, "Server error in fetching bookmark list");
            throw new IOException();
		}

        responseStream.close();
        return tagList;
    }
    
    /**
     * Retrieves a list of all tag bundles for a user from Delicious.
     * 
     * @param account The account being synced.
     * @param context The current application context.
     * @return A list of the users bundles.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    public static ArrayList<Bundle> getBundles(Account account, Context context) 
    	throws IOException, AuthenticationException {
    	
    	ArrayList<Bundle> bundleList = new ArrayList<Bundle>();
    	InputStream responseStream = null;
    	TreeMap<String, String> params = new TreeMap<String, String>();
    	String url = FETCH_BUNDLES_URI;
    	  	
    	responseStream = DeliciousApiCall(url, params, account, context);
    	SaxBundleParser parser = new SaxBundleParser(responseStream);
    	
        try {
        	bundleList = parser.parse();
        } catch (ParseException e) {
        	Log.e(TAG, "Server error in fetching bundle list");
        	throw new IOException();
        }

        responseStream.close();
        return bundleList;
    }
    
    /**
     * Performs an api call to Delicious's http based api methods.
     * 
     * @param url URL of the api method to call.
     * @param params Extra parameters included in the api call, as specified by different methods.
     * @param account The account being synced.
     * @param context The current application context.
     * @return A String containing the response from the server.
     * @throws IOException If a server error was encountered.
     * @throws AuthenticationException If an authentication error was encountered.
     */
    private static InputStream DeliciousApiCall(String url, TreeMap<String, String> params, 
    		Account account, Context context) throws IOException, AuthenticationException{
    	
    	
    	final AccountManager am = AccountManager.get(context);
    	
		if(account == null)
			throw new AuthenticationException();

    	final String username = account.name;
    	String authtoken = null;
    	
    	try {
			authtoken = am.blockingGetAuthToken(account, Constants.AUTHTOKEN_TYPE, false);
		} catch (OperationCanceledException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (AuthenticatorException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		Uri.Builder builder = new Uri.Builder();
		builder.scheme(SCHEME);
		builder.authority(DELICIOUS_AUTHORITY);
		builder.appendEncodedPath(url);
		for(String key : params.keySet()){
			builder.appendQueryParameter(key, params.get(key));
		}
		
		String apiCallUrl = builder.build().toString();
		
		Log.d("apiCallUrl", apiCallUrl);
		final HttpGet post = new HttpGet(apiCallUrl);

		post.setHeader("User-Agent", "DeliciousDroid");
		post.setHeader("Accept-Encoding", "gzip");
    		
		DefaultHttpClient client = (DefaultHttpClient)HttpClientFactory.getThreadSafeClient();
        CredentialsProvider provider = client.getCredentialsProvider();
        Credentials credentials = new UsernamePasswordCredentials(username, authtoken);
        provider.setCredentials(SCOPE, credentials);
        
        client.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0);
               
        final HttpResponse resp = client.execute(post);
        
        final int statusCode = resp.getStatusLine().getStatusCode();

    	if (statusCode == HttpStatus.SC_OK) {
    		
    		final HttpEntity entity = resp.getEntity();
    		
    		InputStream instream = entity.getContent();
    		
    		final Header encoding = entity.getContentEncoding();
    		
    		if(encoding != null && encoding.getValue().equalsIgnoreCase("gzip")) {
    			instream = new GZIPInputStream(instream);
    		}
    		
    		return instream;
    	} else if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
    		throw new AuthenticationException();
    	} else {
    		throw new IOException();
    	}
    }
    
    /**
     * Converts an InputStream to a string.
     * 
     * @param is The InputStream to convert.
     * @return The String retrieved from the InputStream.
     */
    private static String convertStreamToString(InputStream is) {
        /*
         * To convert the InputStream to String we use the BufferedReader.readLine()
         * method. We iterate until the BufferedReader return null which means
         * there's no more data to read. Each line will appended to a StringBuilder
         * and returned as String.
         */
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
 
        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

} public class TooManyRequestsException extends Exception {

	private static final long serialVersionUID = 1L;
	private int backoff = 0;
	
	public int getBackoff(){
		return backoff;
	}
	
	public TooManyRequestsException(){
		
	}
	
	public TooManyRequestsException(int backoff){
		this.backoff = backoff;
	}
} public class BookmarkManager {
	
	public static CursorLoader GetBookmarks(String username, String tagname, String sortorder, Context context){
		final String[] projection = new String[] {Bookmark._ID, Bookmark.Url, Bookmark.Description, 
				Bookmark.Meta, Bookmark.Tags, Bookmark.Shared, Bookmark.Synced, Bookmark.Deleted};
		String selection = null;
		ArrayList<String> selectionList = new ArrayList<String>();
		final ArrayList<String> queryList = new ArrayList<String>();


		if(tagname != null && tagname != "") {
			String[] tagList = tagname.split(",");

			for(String s : tagList) {
				queryList.add("(" + Bookmark.Tags + " LIKE ? OR " +
					Bookmark.Tags + " LIKE ? OR " +
					Bookmark.Tags + " LIKE ? OR " +
					Bookmark.Tags + " = ?)");

				selectionList.add("% " + s + " %");
				selectionList.add("% " + s);
				selectionList.add(s + " %");
				selectionList.add(s);
			}
			selection = TextUtils.join(" OR ", queryList) + " AND " +
				Bookmark.Account + "=?";

			selectionList.add(username);
		} else {
			selectionList.add(username);
			selection = Bookmark.Account + "=?";
		}

		selection += " AND " + Bookmark.Deleted + "=0";
		
		return new CursorLoader(context, Bookmark.CONTENT_URI, projection, selection, selectionList.toArray(new String[]{}), sortorder);
	}
	
	public static ArrayList<Bookmark> GetLocalBookmarks(String username, Context context){
		ArrayList<Bookmark> bookmarkList = new ArrayList<Bookmark>();
		final String[] projection = new String[] {Bookmark._ID, Bookmark.Url, Bookmark.Description, Bookmark.Notes, Bookmark.Hash,
				Bookmark.Meta, Bookmark.Tags, Bookmark.Shared, Bookmark.Synced, Bookmark.Deleted};
		String selection = null;
		String[] selectionargs = new String[]{username};
		
		selectionargs = new String[]{username};
		selection = Bookmark.Account + "=? AND " + Bookmark.Synced + "=0 AND " + Bookmark.Deleted + "=0";
		
		Uri bookmarks = Bookmark.CONTENT_URI;

		Cursor c = context.getContentResolver().query(bookmarks, projection, selection, selectionargs, null);				

		if(c.moveToFirst()){
			int idColumn = c.getColumnIndex(Bookmark._ID);
			int urlColumn = c.getColumnIndex(Bookmark.Url);
			int descriptionColumn = c.getColumnIndex(Bookmark.Description);
			int tagsColumn = c.getColumnIndex(Bookmark.Tags);
			int metaColumn = c.getColumnIndex(Bookmark.Meta);
			int shareColumn = c.getColumnIndex(Bookmark.Shared);
			int notesColumn = c.getColumnIndex(Bookmark.Notes);
			int hashColumn = c.getColumnIndex(Bookmark.Hash);

			do {

				Bookmark b = new Bookmark(c.getInt(idColumn), "", c.getString(urlColumn), 
						c.getString(descriptionColumn), c.getString(notesColumn), c.getString(tagsColumn), c.getString(hashColumn), 
						c.getString(metaColumn), 0, c.getInt(shareColumn) == 0 ? false : true, false, false);

				bookmarkList.add(b);

			} while(c.moveToNext());

		}
		c.close();
		return bookmarkList;
	}
	
	public static ArrayList<Bookmark> GetDeletedBookmarks(String username, Context context){
		ArrayList<Bookmark> bookmarkList = new ArrayList<Bookmark>();
		final String[] projection = new String[] {Bookmark._ID, Bookmark.Url, Bookmark.Description, Bookmark.Notes, Bookmark.Hash,
				Bookmark.Meta, Bookmark.Tags, Bookmark.Shared, Bookmark.Synced, Bookmark.Deleted};
		String selection = null;
		String[] selectionargs = new String[]{username};
		
		selectionargs = new String[]{username};
		selection = Bookmark.Account + "=? AND " + Bookmark.Synced + "=0 AND " + Bookmark.Deleted + "=1";
		
		Uri bookmarks = Bookmark.CONTENT_URI;

		Cursor c = context.getContentResolver().query(bookmarks, projection, selection, selectionargs, null);				

		if(c.moveToFirst()){
			int idColumn = c.getColumnIndex(Bookmark._ID);
			int urlColumn = c.getColumnIndex(Bookmark.Url);
			int descriptionColumn = c.getColumnIndex(Bookmark.Description);
			int tagsColumn = c.getColumnIndex(Bookmark.Tags);
			int metaColumn = c.getColumnIndex(Bookmark.Meta);
			int shareColumn = c.getColumnIndex(Bookmark.Shared);
			int notesColumn = c.getColumnIndex(Bookmark.Notes);
			int hashColumn = c.getColumnIndex(Bookmark.Hash);

			do {

				Bookmark b = new Bookmark(c.getInt(idColumn), "", c.getString(urlColumn), 
						c.getString(descriptionColumn), c.getString(notesColumn), c.getString(tagsColumn), c.getString(hashColumn), 
						c.getString(metaColumn), 0, c.getInt(shareColumn) == 0 ? false : true, false, true);

				bookmarkList.add(b);

			} while(c.moveToNext());

		}
		c.close();
		return bookmarkList;
	}
	
	public static Bookmark GetById(int id, Context context) throws ContentNotFoundException {		
		final String[] projection = new String[] {Bookmark.Account, Bookmark.Url, Bookmark.Description, Bookmark.Notes, Bookmark.Time, Bookmark.Tags, Bookmark.Hash, Bookmark.Meta, Bookmark.Shared, Bookmark.Synced, Bookmark.Deleted};
		String selection = BaseColumns._ID + "=?";
		final String[] selectionargs = new String[]{Integer.toString(id)};
		selection += " AND " + Bookmark.Deleted + "=0";
		
		Cursor c = context.getContentResolver().query(Bookmark.CONTENT_URI, projection, selection, selectionargs, null);				
		
		if(c.moveToFirst()){
			final int accountColumn = c.getColumnIndex(Bookmark.Account);
			final int urlColumn = c.getColumnIndex(Bookmark.Url);
			final int descriptionColumn = c.getColumnIndex(Bookmark.Description);
			final int notesColumn = c.getColumnIndex(Bookmark.Notes);
			final int tagsColumn = c.getColumnIndex(Bookmark.Tags);
			final int hashColumn = c.getColumnIndex(Bookmark.Hash);
			final int metaColumn = c.getColumnIndex(Bookmark.Meta);
			final int timeColumn = c.getColumnIndex(Bookmark.Time);
			final int shareColumn = c.getColumnIndex(Bookmark.Shared);
			final int syncedColumn = c.getColumnIndex(Bookmark.Synced);
			final int deletedColumn = c.getColumnIndex(Bookmark.Deleted);
			
			final boolean share = c.getInt(shareColumn) == 0 ? false : true;
			final boolean synced = c.getInt(syncedColumn) == 0 ? false : true;
			final boolean deleted = c.getInt(deletedColumn) == 0 ? false : true;

			Bookmark b = new Bookmark(id, c.getString(accountColumn), c.getString(urlColumn), 
				c.getString(descriptionColumn), c.getString(notesColumn), c.getString(tagsColumn),
				c.getString(hashColumn), c.getString(metaColumn), c.getLong(timeColumn), share, synced, deleted);
			
			c.close();
			
			return b;
		} else {
			c.close();
			throw new ContentNotFoundException();
		}
	}
	
	public static Bookmark GetByUrl(String url, Context context) throws ContentNotFoundException {		
		final String[] projection = new String[] {Bookmark._ID, Bookmark.Account, Bookmark.Url, Bookmark.Description, Bookmark.Notes, Bookmark.Time, Bookmark.Tags, Bookmark.Hash, Bookmark.Meta, Bookmark.Shared, Bookmark.Synced, Bookmark.Deleted};
		String selection = Bookmark.Url + "=?";
		final String[] selectionargs = new String[]{ url };
		selection += " AND " + Bookmark.Deleted + "=0";
		
		Cursor c = context.getContentResolver().query(Bookmark.CONTENT_URI, projection, selection, selectionargs, null);				
		
		if(c.moveToFirst()){
			final int idColumn = c.getColumnIndex(Bookmark._ID);
			final int accountColumn = c.getColumnIndex(Bookmark.Account);
			final int urlColumn = c.getColumnIndex(Bookmark.Url);
			final int descriptionColumn = c.getColumnIndex(Bookmark.Description);
			final int notesColumn = c.getColumnIndex(Bookmark.Notes);
			final int tagsColumn = c.getColumnIndex(Bookmark.Tags);
			final int hashColumn = c.getColumnIndex(Bookmark.Hash);
			final int metaColumn = c.getColumnIndex(Bookmark.Meta);
			final int timeColumn = c.getColumnIndex(Bookmark.Time);
			final int shareColumn = c.getColumnIndex(Bookmark.Shared);
			final int syncedColumn = c.getColumnIndex(Bookmark.Synced);
			final int deletedColumn = c.getColumnIndex(Bookmark.Deleted);
			
			final boolean share = c.getInt(shareColumn) == 0 ? false : true;
			final boolean synced = c.getInt(syncedColumn) == 0 ? false : true;
			final boolean deleted = c.getInt(deletedColumn) == 0 ? false : true;

			Bookmark b = new Bookmark(c.getInt(idColumn), c.getString(accountColumn), c.getString(urlColumn), 
				c.getString(descriptionColumn), c.getString(notesColumn), c.getString(tagsColumn),
				c.getString(hashColumn), c.getString(metaColumn), c.getLong(timeColumn), share, synced, deleted);
			
			c.close();
			
			return b;
		} else {
			c.close();
			throw new ContentNotFoundException();
		}
	}
	
	public static void AddBookmark(Bookmark bookmark, String account, Context context) {
		final String url = bookmark.getUrl();
		
		String hash = "";
		if(bookmark.getHash() == null || bookmark.getHash() == ""){
			hash = Md5Hash.md5(url);
		} else hash = bookmark.getHash();
		
		final ContentValues values = new ContentValues();
		values.put(Bookmark.Description, bookmark.getDescription());
		values.put(Bookmark.Url, url);
		values.put(Bookmark.Notes, bookmark.getNotes());
		values.put(Bookmark.Tags, bookmark.getTagString());
		values.put(Bookmark.Hash, hash);
		values.put(Bookmark.Meta, bookmark.getMeta());
		values.put(Bookmark.Time, bookmark.getTime());
		values.put(Bookmark.Account, account);
		values.put(Bookmark.Shared, bookmark.getShared() ? 1 : 0);
		values.put(Bookmark.Synced, 0);
		values.put(Bookmark.Deleted, 0);
		
		context.getContentResolver().insert(Bookmark.CONTENT_URI, values);
	}
	
	public static void BulkInsert(ArrayList<Bookmark> list, String account, Context context) {
		int bookmarksize = list.size();
		ContentValues[] bcv = new ContentValues[bookmarksize];
		
		for(int i = 0; i < bookmarksize; i++){
			Bookmark b = list.get(i);
			
			ContentValues values = new ContentValues();
			values.put(Bookmark.Description, b.getDescription());
			values.put(Bookmark.Url, b.getUrl());
			values.put(Bookmark.Notes, b.getNotes());
			values.put(Bookmark.Tags, b.getTagString());
			values.put(Bookmark.Hash, b.getHash());
			values.put(Bookmark.Meta, b.getMeta());
			values.put(Bookmark.Time, b.getTime());
			values.put(Bookmark.Account, account);
			values.put(Bookmark.Shared, b.getShared() ? 1 : 0);
			values.put(Bookmark.Synced, true);
			values.put(Bookmark.Deleted, false);
			
			bcv[i] = values;
		}
		
		context.getContentResolver().bulkInsert(Bookmark.CONTENT_URI, bcv);
	}
	
	public static void UpdateBookmark(Bookmark bookmark, String account, Context context){
		final String url = bookmark.getUrl();
		
		String hash = "";
		if(bookmark.getHash() == null || bookmark.getHash() == ""){
			hash = Md5Hash.md5(url);
		} else hash = bookmark.getHash();
		
		final String selection = Bookmark.Hash + "=? AND " + Bookmark.Account + "=?";
		final String[] selectionargs = new String[]{hash, account};
		
		final ContentValues values = new ContentValues();
		values.put(Bookmark.Description, bookmark.getDescription());
		values.put(Bookmark.Url, url);
		values.put(Bookmark.Notes, bookmark.getNotes());
		values.put(Bookmark.Tags, bookmark.getTagString());
		values.put(Bookmark.Meta, bookmark.getMeta());
		
		if(bookmark.getTime() > 0)
			values.put(Bookmark.Time, bookmark.getTime());
		
		values.put(Bookmark.Shared, bookmark.getShared() ? 1 : 0);
		values.put(Bookmark.Synced, false);
		values.put(Bookmark.Deleted, false);
		
		context.getContentResolver().update(Bookmark.CONTENT_URI, values, selection, selectionargs);
	}
	
	public static void SetSynced(Bookmark bookmark, boolean synced, String account, Context context){
		final String url = bookmark.getUrl();
		
		String hash = "";
		if(bookmark.getHash() == null || bookmark.getHash() == ""){
			hash = Md5Hash.md5(url);
		} else hash = bookmark.getHash();
		
		final String selection = Bookmark.Hash + "=? AND " + Bookmark.Account + "=?";
		final String[] selectionargs = new String[]{hash, account};
		
		final ContentValues values = new ContentValues();
		values.put(Bookmark.Synced, synced);
		
		context.getContentResolver().update(Bookmark.CONTENT_URI, values, selection, selectionargs);
	}
	
	public static void LazyDelete(Bookmark bookmark, String account, Context context){
		final String url = bookmark.getUrl();
		
		String hash = "";
		if(bookmark.getHash() == null || bookmark.getHash() == ""){
			hash = Md5Hash.md5(url);
		} else hash = bookmark.getHash();
		
		final String selection = Bookmark.Hash + "=? AND " + Bookmark.Account + "=?";
		final String[] selectionargs = new String[]{hash, account};
		
		final ContentValues values = new ContentValues();
		values.put(Bookmark.Deleted, true);
		values.put(Bookmark.Synced, false);
		
		context.getContentResolver().update(Bookmark.CONTENT_URI, values, selection, selectionargs);
	}

	public static void DeleteBookmark(Bookmark bookmark, Context context){
		final int id = bookmark.getId();
		String selection = "";
		
		if(id > 0) {
			selection = BaseColumns._ID + "=" + id;
		} else {
			selection = Bookmark.Url + "='" + bookmark.getUrl() + "'";
		}
		
		context.getContentResolver().delete(Bookmark.CONTENT_URI, selection, null);
	}
	
	public static void TruncateBookmarks(ArrayList<String> accounts, Context context, boolean inverse){
		
		final ArrayList<String> selectionList = new ArrayList<String>();
		
		final String operator = inverse ? "<>" : "=";
		final String logicalOp = inverse ? " AND " : " OR ";
		
		for(String s : accounts) {
			selectionList.add(Bookmark.Account + " " + operator + " '" + s + "'");
		}
		
		String selection = TextUtils.join(logicalOp, selectionList);
		
		if(accounts.size() > 0)
			selection += " AND " + Bookmark.Synced + "=1";
		else selection += Bookmark.Synced + "=1";
		
		context.getContentResolver().delete(Bookmark.CONTENT_URI, selection, null);
	}
	
	public static CursorLoader SearchBookmarks(String query, String tagname, String username, Context context) {
		final String[] projection = new String[] {Bookmark._ID, Bookmark.Url, Bookmark.Description, 
				Bookmark.Meta, Bookmark.Tags, Bookmark.Shared, Bookmark.Synced, Bookmark.Deleted};
		String selection = null;
		
		final String sortorder = Bookmark.Description + " ASC";
		
		final String[] queryBookmarks = query.split(" ");
		
		final ArrayList<String> queryList = new ArrayList<String>();
		final ArrayList<String> selectionlist = new ArrayList<String>();
		
		if(query != null && query != "" && (tagname == null || tagname == "")) {
			
			
			for(String s : queryBookmarks) {
				queryList.add("(" + Bookmark.Tags + " LIKE ? OR " +
						Bookmark.Description + " LIKE ? OR " +
						Bookmark.Notes + " LIKE ?)");
				selectionlist.add("%" + s + "%");
				selectionlist.add("%" + s + "%");
				selectionlist.add("%" + s + "%");
			}
			selectionlist.add(username);
			
			selection = TextUtils.join(" AND ", queryList) + " AND " +
				Bookmark.Account + "=?";
		} else if(query != null && query != ""){
			for(String s : queryBookmarks) {
				queryList.add("(" + Bookmark.Description + " LIKE ? OR " +
						Bookmark.Notes + " LIKE ?)");
				
				selectionlist.add("%" + s + "%");
				selectionlist.add("%" + s + "%");
			}

			selection = TextUtils.join(" AND ", queryList) +
				" AND " + Bookmark.Account + "=? AND " +
				"(" + Bookmark.Tags + " LIKE ? OR " +
				Bookmark.Tags + " LIKE ? OR " +
				Bookmark.Tags + " LIKE ? OR " +
				Bookmark.Tags + " = ?)";
			
			selectionlist.add(username);
			selectionlist.add("% " + tagname + " %");
			selectionlist.add("% " + tagname);
			selectionlist.add(tagname + " %");
			selectionlist.add(tagname);
		} else {
			selectionlist.add(username);
			selection = Bookmark.Account + "=?";
		}
		
		selection += " AND " + Bookmark.Deleted + "=0";
		
		return new CursorLoader(context, Bookmark.CONTENT_URI, projection, selection, selectionlist.toArray(new String[]{}), sortorder);
	}

	
	public static Bookmark CursorToBookmark(Cursor c) {
		Bookmark b = new Bookmark();
		b.setId(c.getInt(c.getColumnIndex(Bookmark._ID)));
		b.setDescription(c.getString(c.getColumnIndex(Bookmark.Description)));
		b.setUrl(c.getString(c.getColumnIndex(Bookmark.Url)));
		b.setMeta(c.getString(c.getColumnIndex(Bookmark.Meta)));
		b.setTagString(c.getString(c.getColumnIndex(Bookmark.Tags)));
		
		if(c.getColumnIndex(Bookmark.Account) != -1)
			b.setAccount(c.getString(c.getColumnIndex(Bookmark.Account)));
		
		if(c.getColumnIndex(Bookmark.Notes) != -1)
			b.setNotes(c.getString(c.getColumnIndex(Bookmark.Notes)));
		
		if(c.getColumnIndex(Bookmark.Time) != -1)
			b.setTime(c.getLong(c.getColumnIndex(Bookmark.Time)));
		
		if(c.getColumnIndex(Bookmark.Shared) != -1)
			b.setShared(c.getInt(c.getColumnIndex(Bookmark.Shared)) == 1 ? true : false);
		
		if(c.getColumnIndex(Bookmark.Synced) != -1)
			b.setSynced(c.getInt(c.getColumnIndex(Bookmark.Synced)) == 1 ? true : false);
		
		if(c.getColumnIndex(Bookmark.Deleted) != -1)
			b.setDeleted(c.getInt(c.getColumnIndex(Bookmark.Deleted)) == 1 ? true : false);
		
		return b;
	}
} public class TagManager {
	
	public static CursorLoader GetTags(String account, String sortorder, Context context) {		
		final String[] projection = new String[] {Tag._ID, Tag.Name, Tag.Count};
		final String selection = Tag.Account + "=?";
		final String[] selectionargs = new String[]{account};
		
		return new CursorLoader(context, Tag.CONTENT_URI, projection, selection, selectionargs, sortorder);
	}
	
	public static ArrayList<String> GetTagsAsArray(String account, String sortorder, Context context) {	
		String[] projection = new String[] {Tag.Name};
		String selection = Tag.Account + "=?";
		String[] selectionargs = new String[]{account};
		
		ArrayList<String> result = new ArrayList<String>();

		Cursor c = context.getContentResolver().query(Tag.CONTENT_URI, projection, selection, selectionargs, sortorder);
		
		if(c.moveToFirst()){
			int nameColumn = c.getColumnIndex(Tag.Name);
			
			do{
				result.add(c.getString(nameColumn));
			} while(c.moveToNext());
		}
		
		c.close();
		
		return result;
	}
	
	public static void AddTag(Tag tag, String account, Context context){
		final ContentValues values = new ContentValues();
		
		values.put(Tag.Name, tag.getTagName());
		values.put(Tag.Count, tag.getCount());
		values.put(Tag.Account, account);
	
		context.getContentResolver().insert(Tag.CONTENT_URI, values);
	}
	
	public static void BulkInsert(ArrayList<Tag> list, String account, Context context) {
		int tagsize = list.size();
		ContentValues[] tcv = new ContentValues[tagsize];
		
		for(int i = 0; i < tagsize; i++){	
			Tag t = list.get(i);
			
			ContentValues values = new ContentValues();
			
			values.put(Tag.Name, t.getTagName());
			values.put(Tag.Count, t.getCount());
			values.put(Tag.Account, account);
			
			tcv[i] = values;
		}
		
		context.getContentResolver().bulkInsert(Tag.CONTENT_URI, tcv);
	}
	
	public static void UpsertTag(Tag tag, String account, Context context){
		final String[] projection = new String[] {Tag.Name, Tag.Count};
		final String selection = Tag.Name + "=? AND " +
			Tag.Account + "=?";
		final String[] selectionargs = new String[]{tag.getTagName(), account};
		
		final Cursor c = context.getContentResolver().query(Tag.CONTENT_URI, projection, selection, selectionargs, null);
		
		if(c.moveToFirst()){
			final int countColumn = c.getColumnIndex(Tag.Count);
			final int count = c.getInt(countColumn);
			
			tag.setCount(count + 1);
			UpdateTag(tag, account, context);
		} else {
			tag.setCount(1);
			AddTag(tag, account, context);
		}
		c.close();
	}
	
	public static void UpdateTag(Tag tag, String account, Context context){
		
		final String selection = Tag.Name + "=? AND " +
							Tag.Account + "=?";
		final String[] selectionargs = new String[]{tag.getTagName(), account};
		
		final ContentValues values = new ContentValues();
		values.put(Tag.Count, tag.getCount());
		
		context.getContentResolver().update(Tag.CONTENT_URI, values, selection, selectionargs);
	}
	
	public static void UpleteTag(Tag tag, String account, Context context){
		final String[] projection = new String[] {Tag.Name, Tag.Count};
		final String selection = Tag.Name + "=? AND " +
			Tag.Account + "=?";
		final String[] selectionargs = new String[]{tag.getTagName(), account};

		final Cursor c = context.getContentResolver().query(Tag.CONTENT_URI, projection, selection, selectionargs, null);
		
		if(c.moveToFirst()){
			final int countColumn = c.getColumnIndex(Tag.Count);
			final int count = c.getInt(countColumn);
			
			if(count > 1){
				tag.setCount(count - 1);
				UpdateTag(tag, account, context);
			} else {
				DeleteTag(tag, account, context);
			}
		}
		c.close();
	}

	public static void DeleteTag(Tag tag, String account, Context context){
		final String selection = Tag.Name + "=? AND " +
			Tag.Account + "=?";
		final String[] selectionargs = new String[]{tag.getTagName(), account};
		
		context.getContentResolver().delete(Tag.CONTENT_URI, selection, selectionargs);
	}
	
	public static void TruncateTags(String account, Context context){
		
		final String selection = Tag.Account + "=?";
		final String[] selectionargs = new String[]{account};
		
		context.getContentResolver().delete(Tag.CONTENT_URI, selection, selectionargs);
	}
	
	public static void TruncateOldTags(ArrayList<String> accounts, Context context){
		
		final ArrayList<String> selectionList = new ArrayList<String>();
		
		for(String s : accounts) {
			selectionList.add(Tag.Account + " <> '" + s + "'");
		}
		
		final String selection = TextUtils.join(" AND ", selectionList);
		
		context.getContentResolver().delete(Tag.CONTENT_URI, selection, null);
	}
	
	public static CursorLoader SearchTags(String query, String username, Context context) {
		final String[] projection = new String[] { Tag._ID, Tag.Name, Tag.Count };
		String selection = null;
		
		final String sortorder = Tag.Name + " ASC";
		final ArrayList<String> selectionlist = new ArrayList<String>();
		
		ArrayList<String> queryList = new ArrayList<String>();
		
		for(String s : query.split(" ")) {
			queryList.add(Tag.Name + " LIKE ?");
			selectionlist.add("%" + s + "%");
		}
		
		selectionlist.add(username);
		
		if(query != null && query != "") {
			selection = "(" + TextUtils.join(" OR ", queryList) + ")" + 
				" AND " + Tag.Account + "=?";
		} else {
			selection = Tag.Account + "=?";
		}
		
		return new CursorLoader(context, Tag.CONTENT_URI, projection, selection, selectionlist.toArray(new String[]{}), sortorder);
	}
} public static class Bookmark implements BaseColumns, Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 8170498291668576792L;

	public static final Uri CONTENT_URI = Uri.parse("content://" + BookmarkContentProvider.AUTHORITY + "/bookmark");
		
	public static final  String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.deliciousdroid.bookmarks";
		
	public static final String Account = "ACCOUNT";
	public static final String Description = "DESCRIPTION";
	public static final String Url = "URL";
	public static final String Notes = "NOTES";
	public static final String Tags = "TAGS";
	public static final String Hash = "HASH";
	public static final String Meta = "META";
	public static final String Time = "TIME";
	public static final String Shared = "SHARED";
	public static final String Synced = "SYNCED";
	public static final String Deleted = "DELETED";
		
	private int mId = 0;
	private String mAccount = null;
       private String mUrl = null;
       private String mDescription = null;
       private String mNotes = null;
       private String mTags = null;
       private String mHash = null;
       private String mMeta = null;
       private boolean mShared = true;
       private long mTime = 0;
       private boolean mSynced = false;
       private boolean mDeleted = false;

       public int getId(){
       	return mId;
       }
        
       public void setId(int id){
       	mId = id;
       }
        
       public String getUrl() {
           return mUrl;
       }
        
       public void setUrl(String url) {
       	mUrl = url;
       }

       public String getDescription() {
           return mDescription;
       }
        
       public void setDescription(String desc) {
       	mDescription = desc;
       }
        
       public String getNotes(){
       	return mNotes == null ? "" : mNotes;
       }
        
       public void setNotes(String notes) {
       	mNotes = notes;
       }
        
       public String getTagString(){
       	return mTags;
       }
        
       public void setTagString(String tags){
       	mTags = tags;
       }
        
       public ArrayList<Tag> getTags(){
		ArrayList<Tag> result = new ArrayList<Tag>();
			
		if(mTags != null){
			for(String s : mTags.split(" ")) {
				result.add(new Tag(s));
			}
		}
			
		return result;
       }
        
       public String getHash(){
       	return mHash;
       }
        
       public void setHash(String hash) {
       	mHash = hash;
       }

       public String getMeta(){
       	return mMeta;
       }
        
       public void setMeta(String meta) {
       	mMeta = meta;
       }
        
       public long getTime(){
       	return mTime;
       }
        
       public void setTime(long time) {
       	mTime = time;
       }
        
       public boolean getShared(){
       	return mShared;
       }
        
       public void setShared(boolean shared) {
       	mShared = shared;
       }
        
       public String getAccount(){
       	return mAccount;
       }
        
       public void setAccount(String account) {
       	mAccount = account;
       }
        
       public boolean getSynced(){
       	return mSynced;
       }
        
       public void setSynced(boolean synced){
       	mSynced = synced;
       }
        
       public boolean getDeleted(){
       	return mDeleted;
       }
        
       public void setDeleted(boolean deleted){
       	mDeleted = deleted;
       }
        
       public Bookmark() {
       }
        
       public Bookmark(int id) {
       	mId = id;
       }
        
       public Bookmark(String url) {
           mUrl = url;
       }
        
       public Bookmark(String url, String description, String notes, String tags, boolean priv, long time) {
           mUrl = url;
           mDescription = description;
           mNotes = notes;
           mTags = tags;
           mShared = priv;
           mTime = time;
       }
        
       public Bookmark(int id, String account, String url, String description, String notes, String tags, String hash, String meta, long time, boolean share, boolean synced, boolean deleted) {
           mId = id;
       	mUrl = url;
           mDescription = description;
           mNotes = notes;
           mTags = tags;
           mHash = hash;
           mMeta = meta;
           mTime = time;
           mAccount = account;
           mShared = share;
           mSynced = synced;
           mDeleted = deleted;
       }
        
       public Bookmark copy() {
       	Bookmark b = new Bookmark();
       	b.mAccount = this.mAccount;
       	b.mDescription = this.mDescription;
       	b.mHash = this.mHash;
       	b.mId = this.mId;
       	b.mMeta = this.mMeta;
       	b.mNotes = this.mNotes;
       	b.mShared = this.mShared;
       	b.mTags = this.mTags;
       	b.mTime = this.mTime;
       	b.mUrl = this.mUrl;
       	b.mSynced = this.mSynced;
       	b.mDeleted = this.mDeleted;
       	return b;
       }
        
       public void clear() {
       	this.mAccount = null;
       	this.mDescription = null;
       	this.mHash = null;
       	this.mId = 0;
       	this.mMeta = null;
       	this.mNotes = null;
       	this.mShared = true;
       	this.mTags = null;
       	this.mTime = 0;
       	this.mUrl = null;
       	this.mSynced = false;
       	this.mDeleted = false;
       }
} public static class Tag implements BaseColumns {
	public static final Uri CONTENT_URI = Uri.parse("content://" + 
			BookmarkContentProvider.AUTHORITY + "/tag");
		
	public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.deliciousdroid.tags";
		
	public static final String Name = "NAME";
	public static final String Count = "COUNT";
	public static final String Account = "ACCOUNT";
		
       private String mTagName;
       private int mCount = 0;
       private int mId = 0;
       private String mType = null;

       public int getId(){
       	return mId;
       }
        
       public String getTagName() {
           return mTagName;
       }
        
       public void setTagName(String tagName) {
       	mTagName = tagName;
       }

       public int getCount() {
           return mCount;
       }
        
       public String getType() {
           return mType;
       }
        
       public void setType(String type) {
       	mType = type;
       }
        
       public void setCount(int count) {
       	mCount = count;
       }
        
       public Tag() {
        	
       }
        
       public Tag(String tagName) {
           mTagName = tagName;
       }

       public Tag(String tagName, int count) {
           mTagName = tagName;
           mCount = count;
       }
        
       public Tag copy(){
       	Tag t = new Tag();
       	t.mCount = this.mCount;
       	t.mId = this.mId;
       	t.mTagName = this.mTagName;
       	t.mType = this.mType;
       	return t;
       }
} public class TagSpan extends ClickableSpan {
    public interface OnTagClickListener {
            public void onTagClick(String tag);
    }

    private final String mTag;
    private OnTagClickListener mOnTagClickListener;

    public TagSpan(String tag) {
            super();
            if (tag == null) {
                    throw new NullPointerException();
            }
            mTag = tag;
    }

    @Override
    public void onClick(View widget) {
            if (mOnTagClickListener != null) {
                    mOnTagClickListener.onTagClick(mTag);
            }
    }

    public OnTagClickListener getOnTagClickListener() {
            return mOnTagClickListener;
    }

    public void setOnTagClickListener(OnTagClickListener onTagClickListener) {
            mOnTagClickListener = onTagClickListener;
    }
} public class SpaceTokenizer implements Tokenizer {

	public int findTokenStart(CharSequence text, int cursor) {
		int i = cursor;

		while (i > 0 && text.charAt(i - 1) != ' ') {
			i--;
		}
		while (i < cursor && text.charAt(i) == ' ') {
			i++;
		}

		return i;
	}

	public int findTokenEnd(CharSequence text, int cursor) {
		int i = cursor;
		int len = text.length();

		while (i < len) {
			if (text.charAt(i) == ' ') {
				return i;
			} else {
				i++;
			}
		}

		return len;
	}

	public CharSequence terminateToken(CharSequence text) {
		int i = text.length();

		while (i > 0 && text.charAt(i - 1) == ' ') {
			i--;
		}

		if (i > 0 && text.charAt(i - 1) == ' ') {
			return text;
		} else {
			if (text instanceof Spanned) {
				SpannableString sp = new SpannableString(text + " ");
				TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
						Object.class, sp, 0);
				return sp;
			} else {
				return text + " ";
			}
		}
	}
}
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import org.apache.http.auth.AuthenticationException;
import com.deliciousdroid.R;
import com.deliciousdroid.action.GetWebpageTitleTask;
import com.deliciousdroid.activity.FragmentBaseActivity;
import com.deliciousdroid.client.DeliciousApi;
import com.deliciousdroid.client.TooManyRequestsException;
import com.deliciousdroid.platform.BookmarkManager;
import com.deliciousdroid.platform.TagManager;
import com.deliciousdroid.providers.BookmarkContent.Bookmark;
import com.deliciousdroid.providers.TagContent.Tag;
import com.deliciousdroid.ui.TagSpan;
import com.deliciousdroid.util.SpaceTokenizer;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnFocusChangeListener;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.MultiAutoCompleteTextView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
/*
 * DeliciousDroid - http://code.google.com/p/DeliciousDroid/
 *
 * Copyright (C) 2010 Matt Schmidt
 *
 * DeliciousDroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 3 of the License,
 * or (at your option) any later version.
 *
 * DeliciousDroid is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with DeliciousDroid; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

package com.deliciousdroid.fragment;





public class AddBookmarkFragment extends Fragment {

	private FragmentBaseActivity base;
	
	private EditText mEditUrl;
	private EditText mEditDescription;
	private ProgressBar mDescriptionProgress;
	private EditText mEditNotes;
	private MultiAutoCompleteTextView mEditTags;
	private TextView mRecommendedTags;
	private ProgressBar mRecommendedProgress;
	private TextView mPopularTags;
	private ProgressBar mPopularProgress;
	private CompoundButton mPrivate;
	private Bookmark bookmark;
	private Bookmark oldBookmark;
	private Boolean update = false;
	
	private long updateTime = 0;
	
	private AsyncTask<String, Integer, String> titleTask;
	private AsyncTask<String, Integer, ArrayList<Tag>> tagTask;