/*
* Tweetings - Twitter client for Android
*
* Copyright (C) 2012-2013 RBD Solutions Limited <apps@tweetings.net>
* Copyright (C) 2012 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.dwdesign.tweetings.fragment;
import static com.dwdesign.tweetings.util.Utils.getActivatedAccountIds;
import static com.dwdesign.tweetings.util.Utils.getBiggerTwitterProfileImage;
import static com.dwdesign.tweetings.util.Utils.openUserProfile;
import static com.dwdesign.tweetings.util.Utils.parseURL;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;
import com.dwdesign.popupmenu.PopupMenu;
import com.dwdesign.popupmenu.PopupMenu.OnMenuItemClickListener;
import com.dwdesign.tweetings.HttpClientFactory;
import com.dwdesign.tweetings.R;
import com.dwdesign.tweetings.activity.HomeActivity;
import com.dwdesign.tweetings.app.TweetingsApplication;
import com.dwdesign.tweetings.model.Panes;
import com.dwdesign.tweetings.provider.TweetStore.Accounts;
import com.dwdesign.tweetings.provider.TweetStore.DirectMessages;
import com.dwdesign.tweetings.provider.TweetStore.Mentions;
import com.dwdesign.tweetings.provider.TweetStore.Statuses;
import com.dwdesign.tweetings.util.ArrayUtils;
import com.dwdesign.tweetings.util.ImageLoaderWrapper;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;
public class AccountsFragment extends BaseListFragment implements LoaderCallbacks<Cursor>, OnItemLongClickListener,
OnMenuItemClickListener, Panes.Left {
private SharedPreferences mPreferences;
private static TweetingsApplication mApplication;
private static ContentResolver mResolver;
private AccountsAdapter mAdapter;
private ListView mListView;
private PopupMenu mPopupMenu;
private int mSelectedColor;
private long mSelectedUserId;
private Cursor mCursor;
private boolean mActivityFirstCreated;
private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
final String action = intent.getAction();
if (BROADCAST_ACCOUNT_LIST_DATABASE_UPDATED.equals(action)) {
if (getActivity() == null) return;
getLoaderManager().restartLoader(0, null, AccountsFragment.this);
if (getActivity() instanceof HomeActivity) {
((HomeActivity) getActivity()).checkDefaultAccountSet();
}
}
}
};
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mApplication = getApplication();
mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
mResolver = getContentResolver();
mAdapter = new AccountsAdapter(getActivity());
getLoaderManager().initLoader(0, null, this);
mListView = getListView();
mListView.setOnItemLongClickListener(this);
setListAdapter(mAdapter);
final Bundle args = getArguments();
String bufferCode = null;
if (args != null) {
bufferCode = args.getString(INTENT_KEY_BUFFERAPP_CODE);
if (bufferCode != null) {
authoriseBuffer(bufferCode);
}
}
}
private class AuthoriseBufferTask extends AsyncTask<String, Void, String> {
private ProgressDialog dialog;
// can use UI thread here
protected void onPreExecute() {
dialog = new ProgressDialog(AccountsFragment.this.getActivity());
dialog.setMessage(getString(R.string.buffer_authorising));
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
}
// automatically done on worker thread (separate from UI thread)
protected String doInBackground(final String... args) {
String url = AccountsFragment.this.bufferAuthorise(args[0]);
return url;
}
// can use UI thread here
protected void onPostExecute(final String result) {
//if (this.dialog.isShowing()) {
//this.dialog.dismiss();
//}
if (dialog.isShowing()) {
dialog.dismiss();
}
if (result == null) {
Dialog superSimpleDlg = new Dialog(AccountsFragment.this.getActivity());
superSimpleDlg.setTitle(R.string.buffer_authorising_error);
superSimpleDlg.show();
}
else {
AccountsFragment.this.bufferComplete(result);
}
}
}
public String bufferAuthorise(String code) {
String jsonObject = null;
HttpClient client = HttpClientFactory.getThreadSafeClient();
HttpPost post = new HttpPost("https://api.bufferapp.com/1/oauth2/token.json");
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
nameValuePairs.add(new BasicNameValuePair("client_id", BUFFERAPP_CLIENT_ID));
nameValuePairs.add(new BasicNameValuePair("client_secret", BUFFERAPP_CLIENT_SECRET));
nameValuePairs.add(new BasicNameValuePair("redirect_uri", PROTOCOL_TWEETINGS + AUTHORITY_BUFFERAPP));
nameValuePairs.add(new BasicNameValuePair("code",code));
nameValuePairs.add(new BasicNameValuePair("grant_type","authorization_code"));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = "";
jsonObject = "";
while ((line = rd.readLine()) != null) {
jsonObject += line;
}
} catch (IOException e) {
e.printStackTrace();
}
if (jsonObject != null && !jsonObject.equals("")) {
try {
JSONObject jObject = new JSONObject(jsonObject);
String access_token = jObject.getString("access_token");
if (access_token != null) {
return access_token;
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return "";
}
public void bufferComplete(String result) {
if (result != null && !result.equals("")) {
mPreferences.edit().putString(PREFERENCE_KEY_BUFFERAPP_ACCESS_TOKEN, result).commit();
Toast.makeText(AccountsFragment.this.getActivity(), R.string.buffer_signin_success, Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(AccountsFragment.this.getActivity(), R.string.buffer_authorising_error, Toast.LENGTH_SHORT).show();
}
}
protected void authoriseBuffer(final String code) {
new AuthoriseBufferTask().execute(code);
}
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) {
case REQUEST_SET_COLOR: {
if (resultCode == Activity.RESULT_OK) if (data != null && data.getExtras() != null) {
final int color = data.getIntExtra(Accounts.USER_COLOR, Color.WHITE);
final ContentValues values = new ContentValues();
values.put(Accounts.USER_COLOR, color);
final String where = Accounts.USER_ID + " = " + mSelectedUserId;
mResolver.update(Accounts.CONTENT_URI, values, where, null);
getLoaderManager().restartLoader(0, null, this);
}
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onCreate(final Bundle savedInstanceState) {
mActivityFirstCreated = true;
super.onCreate(savedInstanceState);
}
@Override
public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
final boolean activated_only = false;
final Uri uri = Accounts.CONTENT_URI;
final String[] cols = Accounts.COLUMNS;
final String where = activated_only ? Accounts.IS_ACTIVATED + " = " + 1 : null;
return new CursorLoader(getActivity(), uri, cols, where, null, null);
}
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(R.layout.accounts, null);
}
@Override
public void onDestroy() {
mActivityFirstCreated = true;
super.onDestroy();
}
@Override
public boolean onItemLongClick(final AdapterView<?> adapter, final View view, final int position, final long id) {
if (mApplication.isMultiSelectActive()) return true;
if (isDefaultAccountValid() && mCursor != null && position >= 0 && position < mCursor.getCount()) {
mCursor.moveToPosition(position);
mSelectedColor = mCursor.getInt(mCursor.getColumnIndexOrThrow(Accounts.USER_COLOR));
mSelectedUserId = mCursor.getLong(mCursor.getColumnIndexOrThrow(Accounts.USER_ID));
mPopupMenu = PopupMenu.getInstance(getActivity(), view);
mPopupMenu.inflate(R.menu.action_account);
mPopupMenu.setOnMenuItemClickListener(this);
mPopupMenu.show();
return true;
}
return false;
}
@Override
public void onListItemClick(final ListView l, final View v, final int position, final long id) {
if (mApplication.isMultiSelectActive()) return;
if (mCursor != null && position >= 0 && position < mCursor.getCount()) {
mCursor.moveToPosition(position);
final long user_id = mCursor.getLong(mCursor.getColumnIndexOrThrow(Accounts.USER_ID));
final boolean is_activated = mCursor.getInt(mCursor.getColumnIndexOrThrow(Accounts.IS_ACTIVATED)) == 1;
if (isDefaultAccountValid()) {
if (is_activated) {
openUserProfile(getActivity(), user_id, user_id, null);
}
else {
long default_id = mPreferences.getLong(PREFERENCE_KEY_DEFAULT_ACCOUNT_ID, -1);
openUserProfile(getActivity(), default_id, user_id, null);
}
} else {
setDefaultAccount(user_id);
}
}
super.onListItemClick(l, v, position, id);
}
@Override
public void onLoaderReset(final Loader<Cursor> loader) {
mCursor = null;
mAdapter.swapCursor(null);
}
@Override
public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
mCursor = data;
mAdapter.swapCursor(data);
final SparseBooleanArray checked = new SparseBooleanArray();
data.moveToFirst();
//if (mSelectedIds.size() == 0) {
while (!data.isAfterLast()) {
final boolean is_activated = data.getInt(data.getColumnIndexOrThrow(Accounts.IS_ACTIVATED)) == 1;
final long user_id = data.getLong(data.getColumnIndexOrThrow(Accounts.USER_ID));
//if (is_activated) {
//mSelectedIds.add(user_id);
//}
//View v = (View) getListView().getItemAtPosition(data.getPosition());
//CheckBox cb = (CheckBox) v.findViewById(R.id.checkbox);
//cb.setChecked(is_activated);
//mAdapter.setItemChecked(data.getPosition(), is_activated);
data.moveToNext();
}
/*} else {
while (!cursor.isAfterLast()) {
final long user_id = cursor.getLong(cursor.getColumnIndexOrThrow(Accounts.ACCOUNT_ID));
if (mSelectedIds.contains(user_id)) {
checked.put(cursor.getPosition(), true);
mAdapter.setItemChecked(cursor.getPosition(), true);
}
cursor.moveToNext();
}
}*/
}
@Override
public boolean onMenuItemClick(final MenuItem item) {
if (mSelectedUserId <= 0) return false;
switch (item.getItemId()) {
case MENU_VIEW_PROFILE: {
openUserProfile(getActivity(), mSelectedUserId, mSelectedUserId, null);
break;
}
case MENU_SET_COLOR: {
final Intent intent = new Intent(INTENT_ACTION_SET_COLOR);
final Bundle bundle = new Bundle();
bundle.putInt(Accounts.USER_COLOR, mSelectedColor);
intent.putExtras(bundle);
startActivityForResult(intent, REQUEST_SET_COLOR);
break;
}
case MENU_SET_AS_DEFAULT: {
setDefaultAccount(mSelectedUserId);
break;
}
case MENU_DELETE: {
mResolver.delete(Accounts.CONTENT_URI, Accounts.USER_ID + " = " + mSelectedUserId, null);
// Also delete tweets related to the account we previously
// deleted.
mResolver.delete(Statuses.CONTENT_URI, Statuses.ACCOUNT_ID + " = " + mSelectedUserId, null);
mResolver.delete(Mentions.CONTENT_URI, Mentions.ACCOUNT_ID + " = " + mSelectedUserId, null);
mResolver.delete(DirectMessages.Inbox.CONTENT_URI, DirectMessages.ACCOUNT_ID + " = " + mSelectedUserId,
null);
mResolver.delete(DirectMessages.Outbox.CONTENT_URI,
DirectMessages.ACCOUNT_ID + " = " + mSelectedUserId, null);
if (getActivatedAccountIds(getActivity()).length > 0) {
getLoaderManager().restartLoader(0, null, AccountsFragment.this);
} else {
getActivity().finish();
}
break;
}
}
return super.onContextItemSelected(item);
}
@Override
public void onStart() {
super.onStart();
final IntentFilter filter = new IntentFilter(BROADCAST_ACCOUNT_LIST_DATABASE_UPDATED);
registerReceiver(mStatusReceiver, filter);
if (!mActivityFirstCreated) {
getLoaderManager().restartLoader(0, null, this);
}
}
@Override
public void onStop() {
unregisterReceiver(mStatusReceiver);
if (mPopupMenu != null) {
mPopupMenu.dismiss();
}
mActivityFirstCreated = false;
super.onStop();
}
private boolean isDefaultAccountValid() {
final long default_account_id = mPreferences.getLong(PREFERENCE_KEY_DEFAULT_ACCOUNT_ID, -1);
if (default_account_id == -1) return false;
final long[] activated_ids = getActivatedAccountIds(getActivity());
return ArrayUtils.contains(activated_ids, default_account_id);
}
private void setDefaultAccount(final long account_id) {
mPreferences.edit().putLong(PREFERENCE_KEY_DEFAULT_ACCOUNT_ID, account_id).commit();
mAdapter.notifyDataSetChanged();
if (getActivity() instanceof HomeActivity) {
((HomeActivity) getActivity()).checkDefaultAccountSet();
}
}
public static class ViewHolder {
public final ImageView profile_image;
private final View content, default_indicator;
private final CheckBox checkbox;
private long account_id;
private boolean default_status;
public ViewHolder(View view) {
content = view;
profile_image = (ImageView) view.findViewById(android.R.id.icon);
default_indicator = view.findViewById(android.R.id.text2);
checkbox = (CheckBox) view.findViewById(R.id.checkbox);
}
public void setAccountId(long account_ids) {
account_id = account_ids;
}
public void setAccountColor(int color) {
content.getBackground().mutate().setColorFilter(color, PorterDuff.Mode.MULTIPLY);
content.invalidate();
}
public void setActivated(boolean is_activated) {
default_status = is_activated;
checkbox.setChecked(is_activated);
if (is_activated == true) {
checkbox.setText(mApplication.getString(R.string.is_activated));
}
else {
checkbox.setText(mApplication.getString(R.string.is_not_activated));
}
checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (default_status != isChecked) {
ContentValues values;
values = new ContentValues();
if (buttonView.isChecked()) {
values.put(Accounts.IS_ACTIVATED, 1);
}
else {
values.put(Accounts.IS_ACTIVATED, 0);
}
final String where = Accounts.USER_ID + " = " + account_id;
mResolver.update(Accounts.CONTENT_URI, values, where, null);
}
}
});
}
public void setIsDefault(boolean is_default) {
default_indicator.setVisibility(is_default ? View.VISIBLE : View.GONE);
}
}
static class AccountsAdapter extends SimpleCursorAdapter {
private final ImageLoaderWrapper mProfileImageLoader;
private final SharedPreferences mPreferences;
private int mUserColorIdx, mProfileImageIdx, mUserIdIdx, mIsActive;
private long mDefaultAccountId;
private final boolean mDisplayHiResProfileImage;
public AccountsAdapter(final Context context) {
super(context, R.layout.account_list_item, null, new String[] { Accounts.USERNAME },
new int[] { android.R.id.text1 }, 0);
final TweetingsApplication application = TweetingsApplication.getInstance(context);
mProfileImageLoader = application.getImageLoaderWrapper();
mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
mDisplayHiResProfileImage = context.getResources().getBoolean(R.bool.hires_profile_image);
}
@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
final int color = cursor.getInt(mUserColorIdx);
final ViewHolder holder = (ViewHolder) view.getTag();
holder.setAccountColor(color);
if (cursor.getInt(mIsActive) == 1) {
holder.setActivated(true);
}
else {
holder.setActivated(false);
}
holder.setAccountId(cursor.getLong(mUserIdIdx));
holder.setIsDefault(mDefaultAccountId != -1 && mDefaultAccountId == cursor.getLong(mUserIdIdx));
final String profile_image_url_string = cursor.getString(mProfileImageIdx);
if (mDisplayHiResProfileImage) {
mProfileImageLoader.displayProfileImage(holder.profile_image, getBiggerTwitterProfileImage(profile_image_url_string));
} else {
mProfileImageLoader.displayProfileImage(holder.profile_image, profile_image_url_string);
}
super.bindView(view, context, cursor);
}
@Override
public View newView(final Context context, final Cursor cursor, final ViewGroup parent) {
final View view = super.newView(context, cursor, parent);
final ViewHolder viewholder = new ViewHolder(view);
view.setTag(viewholder);
return view;
}
@Override
public void notifyDataSetChanged() {
mDefaultAccountId = mPreferences.getLong(PREFERENCE_KEY_DEFAULT_ACCOUNT_ID, -1);
super.notifyDataSetChanged();
}
@Override
public Cursor swapCursor(final Cursor cursor) {
if (cursor != null) {
mUserColorIdx = cursor.getColumnIndex(Accounts.USER_COLOR);
mProfileImageIdx = cursor.getColumnIndex(Accounts.PROFILE_IMAGE_URL);
mUserIdIdx = cursor.getColumnIndex(Accounts.USER_ID);
mIsActive = cursor.getColumnIndex(Accounts.IS_ACTIVATED);
}
return super.swapCursor(cursor);
}
}
}