/*
* Firetweet - Twitter client for Android
*
* Copyright (C) 2012-2014 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 org.getlantern.firetweet.activity.support;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import org.getlantern.firetweet.R;
import org.getlantern.firetweet.app.FiretweetApplication;
import org.getlantern.firetweet.fragment.support.SupportProgressDialogFragment;
import org.getlantern.firetweet.loader.support.ParcelableUserLoader;
import org.getlantern.firetweet.model.ParcelableUser;
import org.getlantern.firetweet.model.SingleResponse;
import org.getlantern.firetweet.util.AsyncTaskManager;
import org.getlantern.firetweet.util.AsyncTaskUtils;
import org.getlantern.firetweet.util.AsyncTwitterWrapper.UpdateProfileBannerImageTask;
import org.getlantern.firetweet.util.AsyncTwitterWrapper.UpdateProfileImageTask;
import org.getlantern.firetweet.util.MediaLoaderWrapper;
import org.getlantern.firetweet.util.ParseUtils;
import org.getlantern.firetweet.util.ThemeUtils;
import org.getlantern.firetweet.util.TwitterWrapper;
import org.getlantern.firetweet.util.accessor.ViewAccessor;
import org.getlantern.firetweet.view.ForegroundColorView;
import org.getlantern.firetweet.view.iface.IExtendedView.OnSizeChangedListener;
import java.io.File;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.User;
import static android.text.TextUtils.isEmpty;
import static org.getlantern.firetweet.util.Utils.createPickImageIntent;
import static org.getlantern.firetweet.util.Utils.createTakePhotoIntent;
import static org.getlantern.firetweet.util.Utils.getTwitterInstance;
import static org.getlantern.firetweet.util.Utils.isMyAccount;
import static org.getlantern.firetweet.util.Utils.showErrorMessage;
public class UserProfileEditorActivity extends BaseActionBarActivity implements OnSizeChangedListener, TextWatcher,
OnClickListener, LoaderCallbacks<SingleResponse<ParcelableUser>> {
private static final int LOADER_ID_USER = 1;
private static final int REQUEST_UPLOAD_PROFILE_IMAGE = 1;
private static final int REQUEST_UPLOAD_PROFILE_BANNER_IMAGE = 2;
private static final int REQUEST_PICK_LINK_COLOR = 3;
private static final int REQUEST_PICK_BACKGROUND_COLOR = 4;
private MediaLoaderWrapper mLazyImageLoader;
private AsyncTaskManager mAsyncTaskManager;
private AsyncTask<Object, Object, ?> mTask;
private ImageView mProfileImageView;
private ImageView mProfileBannerView;
private EditText mEditName, mEditDescription, mEditLocation, mEditUrl;
private View mProgressContainer, mContent;
private View mProfileImageCamera, mProfileImageGallery;
private View mProfileBannerGallery, mProfileBannerRemove;
private View mActionBarOverlay;
private View mCancelButton, mDoneButton;
private View mSetLinkColor, mSetBackgroundColor;
private ForegroundColorView mLinkColor, mBackgroundColor;
private long mAccountId;
private ParcelableUser mUser;
private boolean mUserInfoLoaderInitialized;
private boolean mGetUserInfoCalled;
private Toolbar mToolbar;
@Override
public void beforeTextChanged(final CharSequence s, final int length, final int start, final int end) {
}
@Override
public void onTextChanged(final CharSequence s, final int length, final int start, final int end) {
updateDoneButton();
}
@Override
public void afterTextChanged(final Editable s) {
}
@Override
public int getThemeResourceId() {
return ThemeUtils.getNoActionBarThemeResource(this);
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
final long accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
if (!isMyAccount(this, accountId)) {
finish();
return;
}
mAsyncTaskManager = FiretweetApplication.getInstance(this).getAsyncTaskManager();
mLazyImageLoader = FiretweetApplication.getInstance(this).getMediaLoaderWrapper();
mAccountId = accountId;
setContentView(R.layout.activity_user_profile_editor);
setSupportActionBar(mToolbar);
ViewAccessor.setBackground(mActionBarOverlay, ThemeUtils.getWindowContentOverlay(this));
ViewAccessor.setBackground(mToolbar, ThemeUtils.getActionBarBackground(mToolbar.getContext(),
getCurrentThemeResourceId()));
// setOverrideExitAniamtion(false);
mEditName.addTextChangedListener(this);
mEditDescription.addTextChangedListener(this);
mEditLocation.addTextChangedListener(this);
mEditUrl.addTextChangedListener(this);
mProfileImageView.setOnClickListener(this);
mProfileBannerView.setOnClickListener(this);
mProfileImageCamera.setOnClickListener(this);
mProfileImageGallery.setOnClickListener(this);
mProfileBannerGallery.setOnClickListener(this);
mProfileBannerRemove.setOnClickListener(this);
mCancelButton.setOnClickListener(this);
mDoneButton.setOnClickListener(this);
mSetLinkColor.setOnClickListener(this);
mSetBackgroundColor.setOnClickListener(this);
if (savedInstanceState != null && savedInstanceState.getParcelable(EXTRA_USER) != null) {
final ParcelableUser user = savedInstanceState.getParcelable(EXTRA_USER);
displayUser(user);
mEditName.setText(savedInstanceState.getString(EXTRA_NAME, user.name));
mEditLocation.setText(savedInstanceState.getString(EXTRA_LOCATION, user.location));
mEditDescription.setText(savedInstanceState.getString(EXTRA_DESCRIPTION, user.description_expanded));
mEditUrl.setText(savedInstanceState.getString(EXTRA_URL, user.url_expanded));
} else {
getUserInfo();
}
}
@Override
protected void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(EXTRA_USER, mUser);
outState.putString(EXTRA_NAME, ParseUtils.parseString(mEditName.getText()));
outState.putString(EXTRA_DESCRIPTION, ParseUtils.parseString(mEditDescription.getText()));
outState.putString(EXTRA_LOCATION, ParseUtils.parseString(mEditLocation.getText()));
outState.putString(EXTRA_URL, ParseUtils.parseString(mEditUrl.getText()));
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
public void onClick(final View view) {
final ParcelableUser user = mUser;
if (user == null || (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING))
return;
switch (view.getId()) {
case R.id.profile_image: {
break;
}
case R.id.profile_banner: {
break;
}
case R.id.profile_image_camera: {
final Uri uri = createTempFileUri();
final Intent intent = createTakePhotoIntent(uri, null, null, 1, 1, true);
mTask = new UpdateProfileImageTaskInternal(this, mAsyncTaskManager, mAccountId, uri, true);
startActivityForResult(intent, REQUEST_UPLOAD_PROFILE_IMAGE);
break;
}
case R.id.profile_image_gallery: {
final Uri uri = createTempFileUri();
final Intent intent = createPickImageIntent(uri, null, null, 1, 1, true);
mTask = new UpdateProfileImageTaskInternal(this, mAsyncTaskManager, mAccountId, uri, true);
startActivityForResult(intent, REQUEST_UPLOAD_PROFILE_IMAGE);
break;
}
case R.id.profile_banner_gallery: {
final Uri uri = createTempFileUri();
final Intent intent = createPickImageIntent(uri, null, null, 2, 1, true);
mTask = new UpdateProfileBannerImageTaskInternal(this, mAsyncTaskManager, mAccountId, uri, true);
startActivityForResult(intent, REQUEST_UPLOAD_PROFILE_BANNER_IMAGE);
break;
}
case R.id.profile_banner_remove: {
mTask = new RemoveProfileBannerTaskInternal(user.account_id);
AsyncTaskUtils.executeTask(mTask);
break;
}
case R.id.actionbar_cancel: {
finish();
break;
}
case R.id.actionbar_done: {
final String name = ParseUtils.parseString(mEditName.getText());
final String url = ParseUtils.parseString(mEditUrl.getText());
final String location = ParseUtils.parseString(mEditLocation.getText());
final String description = ParseUtils.parseString(mEditDescription.getText());
final int linkColor = mLinkColor.getColor();
final int backgroundColor = mBackgroundColor.getColor();
mTask = new UpdateProfileTaskInternal(this, mAccountId, mUser, name, url, location,
description, linkColor, backgroundColor);
AsyncTaskUtils.executeTask(mTask);
break;
}
case R.id.set_link_color: {
final Intent intent = new Intent(this, ColorPickerDialogActivity.class);
intent.putExtra(EXTRA_COLOR, user.link_color);
intent.putExtra(EXTRA_ALPHA_SLIDER, false);
startActivityForResult(intent, REQUEST_PICK_LINK_COLOR);
break;
}
case R.id.set_background_color: {
final Intent intent = new Intent(this, ColorPickerDialogActivity.class);
intent.putExtra(EXTRA_COLOR, user.background_color);
intent.putExtra(EXTRA_ALPHA_SLIDER, false);
startActivityForResult(intent, REQUEST_PICK_BACKGROUND_COLOR);
break;
}
}
}
@Override
public Loader<SingleResponse<ParcelableUser>> onCreateLoader(final int id, final Bundle args) {
mProgressContainer.setVisibility(View.VISIBLE);
mContent.setVisibility(View.GONE);
return new ParcelableUserLoader(UserProfileEditorActivity.this, mAccountId, mAccountId, null, getIntent()
.getExtras(), false, false);
}
@Override
public void onLoadFinished(final Loader<SingleResponse<ParcelableUser>> loader,
final SingleResponse<ParcelableUser> data) {
if (data.getData() != null && data.getData().id > 0) {
displayUser(data.getData());
} else if (mUser == null) {
finish();
}
}
@Override
public void onLoaderReset(final Loader<SingleResponse<ParcelableUser>> loader) {
}
@Override
public void onSizeChanged(final View view, final int w, final int h, final int oldw, final int oldh) {
}
@Override
public void onSupportContentChanged() {
super.onSupportContentChanged();
mToolbar = (Toolbar) findViewById(R.id.done_bar);
mProgressContainer = findViewById(R.id.progress_container);
mContent = findViewById(R.id.content);
mProfileBannerView = (ImageView) findViewById(R.id.profile_banner);
mProfileImageView = (ImageView) findViewById(R.id.profile_image);
mEditName = (EditText) findViewById(R.id.name);
mEditDescription = (EditText) findViewById(R.id.description);
mEditLocation = (EditText) findViewById(R.id.location);
mEditUrl = (EditText) findViewById(R.id.url);
mActionBarOverlay = findViewById(R.id.actionbar_overlay);
mProfileImageCamera = findViewById(R.id.profile_image_camera);
mProfileImageGallery = findViewById(R.id.profile_image_gallery);
mProfileBannerGallery = findViewById(R.id.profile_banner_gallery);
mProfileBannerRemove = findViewById(R.id.profile_banner_remove);
mLinkColor = (ForegroundColorView) findViewById(R.id.link_color);
mBackgroundColor = (ForegroundColorView) findViewById(R.id.background_color);
mCancelButton = findViewById(R.id.actionbar_cancel);
mDoneButton = findViewById(R.id.actionbar_done);
mSetLinkColor = findViewById(R.id.set_link_color);
mSetBackgroundColor = findViewById(R.id.set_background_color);
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (resultCode == RESULT_CANCELED) return;
switch (requestCode) {
case REQUEST_UPLOAD_PROFILE_BANNER_IMAGE: {
if (mTask == null || mTask.getStatus() != AsyncTask.Status.PENDING) return;
AsyncTaskUtils.executeTask(mTask);
break;
}
case REQUEST_UPLOAD_PROFILE_IMAGE: {
if (mTask == null || mTask.getStatus() != AsyncTask.Status.PENDING) return;
AsyncTaskUtils.executeTask(mTask);
break;
}
case REQUEST_PICK_LINK_COLOR: {
if (resultCode == ColorPickerDialogActivity.RESULT_OK) {
mLinkColor.setColor(data.getIntExtra(EXTRA_COLOR, 0));
updateDoneButton();
}
break;
}
case REQUEST_PICK_BACKGROUND_COLOR: {
if (resultCode == ColorPickerDialogActivity.RESULT_OK) {
mBackgroundColor.setColor(data.getIntExtra(EXTRA_COLOR, 0));
updateDoneButton();
}
break;
}
}
}
boolean isProfileChanged() {
final ParcelableUser user = mUser;
if (user == null) return true;
if (!stringEquals(mEditName.getText(), user.name)) return true;
if (!stringEquals(mEditDescription.getText(), user.description_expanded)) return true;
if (!stringEquals(mEditLocation.getText(), user.location)) return true;
if (!stringEquals(mEditUrl.getText(), isEmpty(user.url_expanded) ? user.url : user.url_expanded))
return true;
if (mLinkColor.getColor() != user.link_color) return true;
if (mBackgroundColor.getColor() != user.background_color) return true;
return false;
}
private Uri createTempFileUri() {
final File cache_dir = getExternalCacheDir();
final File file = new File(cache_dir, "tmp_image_" + System.currentTimeMillis());
return Uri.fromFile(file);
}
private void displayUser(final ParcelableUser user) {
if (!mGetUserInfoCalled) return;
mGetUserInfoCalled = false;
mUser = user;
if (user != null) {
mProgressContainer.setVisibility(View.GONE);
mContent.setVisibility(View.VISIBLE);
mEditName.setText(user.name);
mEditDescription.setText(user.description_expanded);
mEditLocation.setText(user.location);
mEditUrl.setText(isEmpty(user.url_expanded) ? user.url : user.url_expanded);
mLazyImageLoader.displayProfileImage(mProfileImageView, user.profile_image_url);
final int def_width = getResources().getDisplayMetrics().widthPixels;
mLazyImageLoader.displayProfileBanner(mProfileBannerView, user.profile_banner_url, def_width);
mLinkColor.setColor(user.link_color);
mBackgroundColor.setColor(user.background_color);
} else {
mProgressContainer.setVisibility(View.GONE);
mContent.setVisibility(View.GONE);
}
updateDoneButton();
}
private void getUserInfo() {
final LoaderManager lm = getSupportLoaderManager();
lm.destroyLoader(LOADER_ID_USER);
mGetUserInfoCalled = true;
if (mUserInfoLoaderInitialized) {
lm.restartLoader(LOADER_ID_USER, null, this);
} else {
lm.initLoader(LOADER_ID_USER, null, this);
mUserInfoLoaderInitialized = true;
}
}
private void setUpdateState(final boolean start) {
mEditName.setEnabled(!start);
mEditDescription.setEnabled(!start);
mEditLocation.setEnabled(!start);
mEditUrl.setEnabled(!start);
mProfileImageView.setEnabled(!start);
mProfileImageView.setOnClickListener(start ? null : this);
mProfileBannerView.setEnabled(!start);
mProfileBannerView.setOnClickListener(start ? null : this);
invalidateOptionsMenu();
}
private static boolean stringEquals(final CharSequence str1, final CharSequence str2) {
if (str1 == null || str2 == null) return str1 == str2;
if (str1.length() != str2.length()) return false;
for (int i = 0, j = str1.length(); i < j; i++) {
if (str1.charAt(i) != str2.charAt(i)) return false;
}
return true;
}
private void updateDoneButton() {
mDoneButton.setEnabled(isProfileChanged());
}
static class UpdateProfileTaskInternal extends AsyncTask<Object, Object, SingleResponse<ParcelableUser>> {
private static final String DIALOG_FRAGMENT_TAG = "updating_user_profile";
private final UserProfileEditorActivity mActivity;
private final Handler mHandler;
private final long mAccountId;
private final ParcelableUser mOriginal;
private final String mName;
private final String mUrl;
private final String mLocation;
private final String mDescription;
private final int mLinkColor;
private final int mBackgroundColor;
public UpdateProfileTaskInternal(final UserProfileEditorActivity activity,
final long accountId, final ParcelableUser original,
final String name, final String url, final String location,
final String description, final int linkColor,
final int backgroundColor) {
mActivity = activity;
mHandler = new Handler(activity.getMainLooper());
mAccountId = accountId;
mOriginal = original;
mName = name;
mUrl = url;
mLocation = location;
mDescription = description;
mLinkColor = linkColor;
mBackgroundColor = backgroundColor;
}
@Override
protected SingleResponse<ParcelableUser> doInBackground(final Object... params) {
final Twitter twitter = getTwitterInstance(mActivity, mAccountId, true);
try {
User user = null;
if (isColorChanged()) {
final String linkColor = String.format("%08x", mLinkColor).substring(2);
final String backgroundColor = String.format("%08x", mBackgroundColor).substring(2);
user = twitter.updateProfileColors(backgroundColor, null, linkColor, null, null);
}
if (isProfileChanged()) {
user = twitter.updateProfile(mName, mUrl, mLocation, mDescription);
}
if (user == null) {
// User profile unchanged
return SingleResponse.getInstance();
}
return SingleResponse.getInstance(new ParcelableUser(user, mAccountId));
} catch (TwitterException e) {
return SingleResponse.getInstance(e);
}
}
private boolean isColorChanged() {
final ParcelableUser orig = mOriginal;
if (orig == null) return true;
if (mLinkColor != orig.link_color) return true;
if (mBackgroundColor != orig.background_color) return true;
return false;
}
private boolean isProfileChanged() {
final ParcelableUser orig = mOriginal;
if (orig == null) return true;
if (!stringEquals(mName, orig.name)) return true;
if (!stringEquals(mDescription, isEmpty(orig.description_expanded) ? orig.description_plain : orig.description_expanded))
return true;
if (!stringEquals(mLocation, orig.location)) return true;
if (!stringEquals(mUrl, isEmpty(orig.url_expanded) ? orig.url : orig.url_expanded))
return true;
return false;
}
@Override
protected void onPostExecute(final SingleResponse<ParcelableUser> result) {
super.onPostExecute(result);
final Fragment f = mActivity.getSupportFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG);
if (f instanceof DialogFragment) {
((DialogFragment) f).dismissAllowingStateLoss();
}
}
@Override
protected void onPreExecute() {
mHandler.post(new Runnable() {
@Override
public void run() {
final DialogFragment df = SupportProgressDialogFragment.show(mActivity, DIALOG_FRAGMENT_TAG);
df.setCancelable(false);
}
});
super.onPreExecute();
}
}
class RemoveProfileBannerTaskInternal extends AsyncTask<Object, Object, SingleResponse<Boolean>> {
private final long account_id;
RemoveProfileBannerTaskInternal(final long account_id) {
this.account_id = account_id;
}
@Override
protected SingleResponse<Boolean> doInBackground(final Object... params) {
return TwitterWrapper.deleteProfileBannerImage(UserProfileEditorActivity.this, account_id);
}
@Override
protected void onPostExecute(final SingleResponse<Boolean> result) {
super.onPostExecute(result);
if (result.getData() != null && result.getData()) {
getUserInfo();
Toast.makeText(UserProfileEditorActivity.this, R.string.profile_banner_image_updated,
Toast.LENGTH_SHORT).show();
} else {
showErrorMessage(UserProfileEditorActivity.this, R.string.action_removing_profile_banner_image,
result.getException(), true);
}
setUpdateState(false);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
setUpdateState(true);
}
}
private class UpdateProfileBannerImageTaskInternal extends UpdateProfileBannerImageTask {
public UpdateProfileBannerImageTaskInternal(final Context context, final AsyncTaskManager manager,
final long account_id, final Uri image_uri, final boolean delete_image) {
super(context, manager, account_id, image_uri, delete_image);
}
@Override
protected void onPostExecute(final SingleResponse<ParcelableUser> result) {
super.onPostExecute(result);
setUpdateState(false);
getUserInfo();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
setUpdateState(true);
}
}
private class UpdateProfileImageTaskInternal extends UpdateProfileImageTask {
public UpdateProfileImageTaskInternal(final Context context, final AsyncTaskManager manager,
final long account_id, final Uri image_uri, final boolean delete_image) {
super(context, manager, account_id, image_uri, delete_image);
}
@Override
protected void onPostExecute(final SingleResponse<ParcelableUser> result) {
super.onPostExecute(result);
if (result != null && result.getData() != null) {
displayUser(result.getData());
}
setUpdateState(false);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
setUpdateState(true);
}
}
}