/*
* Copyright (C) 2013 Chris Lacy Licensed under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
* or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package com.tweetlanes.android.core.view;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.tweetlanes.android.core.AppSettings;
import com.tweetlanes.android.core.R;
import com.tweetlanes.android.core.model.ComposeTweetDefault;
import com.tweetlanes.android.core.util.Util;
import org.socialnetlib.android.SocialNetConstant;
import org.tweetalib.android.TwitterFetchResult;
import org.tweetalib.android.TwitterFetchStatus.FinishedCallback;
import org.tweetalib.android.TwitterManager;
import org.tweetalib.android.model.TwitterStatus;
import org.tweetalib.android.model.TwitterStatusUpdate;
import java.io.File;
import java.io.IOException;
public class ComposeTweetFragment extends ComposeBaseFragment {
private static final int THUMBNAIL_WIDTH = 200;
private static final int THUMBNAIL_HEIGHT = 200;
private ImageView mAttachImagePreview;
/*
* (non-Javadoc)
*
* @see
* com.tweetlanes.android.core.view.ComposeBaseFragment#onCreateView(android.
* view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View resultView = super.onCreateView(inflater, container,
savedInstanceState);
mAttachImagePreview = (ImageView) resultView
.findViewById(R.id.statusImage);
mAttachImagePreview.setVisibility(View.GONE);
mAttachImagePreview.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if (mListener != null) {
mListener.onMediaDetach();
}
getComposeTweetDefault().clearMediaFilePath();
mAttachImagePreview.setVisibility(View.GONE);
return true;
}
});
return resultView;
}
/*
*
*/
@Override
protected void setComposeTweetDefault(
ComposeTweetDefault composeTweetDefault) {
super.setComposeTweetDefault(composeTweetDefault);
setMediaPreviewVisibility();
}
/*
*
*/
@Override
protected void updateComposeTweetDefault() {
String currentStatus = mEditText.getText().toString();
if (Util.isValidString(currentStatus)) {
ComposeTweetDefault composeTweetDefault = new ComposeTweetDefault(getApp()
.getCurrentAccountScreenName(), currentStatus,
getInReplyToId(), getMediaFilePath());
setComposeTweetDefault(composeTweetDefault);
}
}
/*
*
*/
@Override
protected String getTweetDefaultDraft() {
if (getComposeTweetDefault() != null) {
String result = getComposeTweetDefault().toString();
if (result != null && !result.equals("")) {
return result;
}
} else {
if (mListener != null) {
String draftAsJsonString = mListener.getDraft();
if (draftAsJsonString != null && !draftAsJsonString.equals("")) {
return draftAsJsonString;
}
}
}
return null;
}
/*
*
*/
@Override
protected void setTweetDefaultFromDraft(String tweetDraftAsJson) {
if (tweetDraftAsJson != null) {
ComposeTweetDefault tweetDefault = new ComposeTweetDefault(
tweetDraftAsJson);
setComposeTweetDefault(tweetDefault);
}
}
/*
*
*/
private final FinishedCallback mOnSetStatusCallback = TwitterManager.get()
.getFetchStatusInstance().new FinishedCallback() {
@Override
public void finished(TwitterFetchResult result, TwitterStatus status) {
/*
* if (mSendingNotification != null) {
* NotificationHelper.get().cancel(getActivity(),
* mSendingNotification); mSendingNotification = null; }
*/
mUpdatingStatus = false;
mEditText.setEnabled(true);
mSendButton.setEnabled(true);
if (result.isSuccessful()) {
releaseFocus(false);
if (mListener != null) {
mListener.saveDraft(null);
mListener.onStatusUpdateSuccess();
}
}
updateStatusHint();
resetScreenRotation();
}
};
/*
*
*/
String getDefaultQuoteStatus(TwitterStatus statusToQuote) {
if (statusToQuote.mStatus.length() > 0) {
String quote;
switch (AppSettings.get().getCurrentQuoteType()) {
case RT:
quote = "RT @" + statusToQuote.getAuthorScreenName() + ": "
+ statusToQuote.mStatus;
break;
case Via:
quote = statusToQuote.mStatus + " (via @"
+ statusToQuote.getAuthorScreenName() + ")";
break;
case Standard:
default:
quote = "\"@" + statusToQuote.getAuthorScreenName() + ": "
+ statusToQuote.mStatus + "\"";
break;
}
return quote;
}
return "";
}
/*
*
*/
Long getInReplyToId() {
if (getComposeTweetDefault() != null) {
return getComposeTweetDefault().getInReplyToStatusId();
}
return null;
}
/*
*
*/
String getMediaFilePath() {
if (getComposeTweetDefault() != null) {
return getComposeTweetDefault().getMediaFilePath();
}
return null;
}
/*
*
*/
@Override
protected void saveCurrentAsDraft() {
ComposeTweetDefault composeDraft = null;
String currentStatus = mEditText.getText().toString();
if (currentStatus != null && !currentStatus.equals("")) {
if (mStatusValidator.getTweetLength(currentStatus) > 0) {
if (getComposeTweetDefault() != null) {
getComposeTweetDefault().updateStatus(currentStatus);
if (!getComposeTweetDefault().isPlaceholderStatus()) {
composeDraft = getComposeTweetDefault();
}
} else {
composeDraft = new ComposeTweetDefault(getApp()
.getCurrentAccountScreenName(), currentStatus);
}
}
}
if (mListener != null) {
mListener.saveDraft(composeDraft == null ? null : composeDraft
.toString());
}
}
/*
* (non-Javadoc)
*
* @see
* com.tweetlanes.android.core.view.ComposeBaseFragment#clearCompose(boolean)
*/
@Override
void clearCompose(boolean saveCurrentTweet) {
super.clearCompose(saveCurrentTweet);
mAttachImagePreview.setVisibility(View.GONE);
}
/*
* (non-Javadoc)
*
* @see
* com.tweetlanes.android.core.view.ComposeBaseFragment#onSendClick(java.lang
* .String)
*/
@Override
protected void onSendClick(String status) {
if (status != null) {
int statusLength = mStatusValidator.getTweetLength(status);
if (statusLength == 0) {
showSimpleAlert(getApp().getCurrentAccount().getSocialNetType() == SocialNetConstant.Type.Twitter ? R
.string.alert_status_empty : R.string.alert_status_empty_adn);
} else if (!mStatusValidator.isValidTweet(status, getMaxPostLength())) {
showSimpleAlert(mStatusValidator.getTweetLength(status) <= getMaxPostLength() ? R.string.alert_status_invalid
: (getApp().getCurrentAccount().getSocialNetType() == SocialNetConstant.Type.Twitter ? R
.string.alert_status_too_long : R.string.alert_status_too_long_adn));
} else if (statusLength > 0) {
TwitterStatusUpdate statusUpdate = new TwitterStatusUpdate(
status, getInReplyToId());
statusUpdate.setMediaFilePath(getMediaFilePath());
mUpdatingStatus = true;
mEditText.setHint(null);
mEditText.setEnabled(false);
mSendButton.setEnabled(false);
TwitterManager.get().setStatus(statusUpdate,
mOnSetStatusCallback);
showToast(getString(R.string.posting_tweet_ongoing));
ComposeTweetDefault currentStatus = new ComposeTweetDefault(
getApp().getCurrentAccountScreenName(), status,
getInReplyToId(), getMediaFilePath());
if (mListener != null) {
mListener.saveDraft(currentStatus.toString());
mListener.onStatusUpdateRequest();
}
setComposeTweetDefault(currentStatus);
updateStatusHint();
}
}
}
/*
*
*/
private String getStatusHint(ComposeTweetDefault composeTweetDefault) {
String hint = null;
if (composeTweetDefault != null && getApp() != null) {
String lastStatus = composeTweetDefault.getStatus();
if (lastStatus != null && lastStatus != "") {
lastStatus = Util.trimLeftRight(lastStatus);
if (composeTweetDefault.getInReplyToStatusId() != null) {
hint = getString(R.string.compose_tweet_reply_to) + " "
+ getStatusHintSnippet(lastStatus, 20);
} else {
SocialNetConstant.Type socialNetType = getApp().getCurrentAccount().getSocialNetType();
String[] words = lastStatus.split(" ");
if (words.length == 1 && words[0].length() > 0
&& words[0].charAt(0) == '@') {
hint = getString(socialNetType == SocialNetConstant.Type.Twitter ? R.string
.compose_tweet_to_user_hint : R.string.compose_tweet_to_user_hint_adn)
+ " " + getStatusHintSnippet(words[0], 18);
} else {
hint = getString(socialNetType == SocialNetConstant.Type.Twitter ? R.string
.compose_tweet_finish : R.string.compose_tweet_finish_adn) + " \""
+ getStatusHintSnippet(lastStatus, 16) + "\"";
super.configureCharacterCountForString(lastStatus);
}
}
}
}
return hint;
}
/*
*
*/
@Override
protected void updateStatusHint() {
if (mUpdatingStatus) {
mEditText.setHint(R.string.posting_tweet_ongoing);
return;
}
String hint = getStatusHint(getComposeTweetDefault());
if (hint == null && mListener != null) {
String draftAsJsonString = mListener.getDraft();
if (draftAsJsonString != null) {
ComposeTweetDefault draft = new ComposeTweetDefault(
draftAsJsonString);
hint = getStatusHint(draft);
setComposeTweetDefault(draft);
}
}
if (hint == null && isAdded()) {
hint = getString(R.string.compose_tweet_default_hint) + getApp().getCurrentAccount().getScreenName() + "?";
configureCharacterCountForString("");
}
mEditText.setHint(hint);
if (mListener != null) {
mListener.onStatusHintUpdate();
}
}
/*
*
*/
void setMediaFilePath(String filePath) {
if (getComposeTweetDefault() == null) {
setComposeTweetDefault(new ComposeTweetDefault(getApp()
.getCurrentAccountScreenName(), null, filePath));
} else {
getComposeTweetDefault().setMediaFilePath(filePath);
}
setMediaPreviewVisibility();
updateStatusHint();
}
/*
*
*/
public void setMediaPreviewVisibility() {
mAttachImagePreview.setVisibility(View.GONE);
if (_mComposeDefault != null && _mComposeDefault.getMediaFilePath() != null) {
File imgFile = new File(_mComposeDefault.getMediaFilePath());
if (imgFile.exists()) {
try {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgFile.getAbsolutePath(), options);
options.inSampleSize = calculateInSampleSize(options, THUMBNAIL_WIDTH,
THUMBNAIL_HEIGHT);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), options);
try {
ExifInterface exif = new ExifInterface(imgFile.getAbsolutePath());
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(exifOrientation);
if (rotationInDegrees != 0f) {
Matrix matrix = new Matrix();
matrix.preRotate(rotationInDegrees);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
}
} catch (IOException e) {
Log.e(ComposeTweetFragment.class.getName(), "could not get EXIF info", e);
}
mAttachImagePreview.setImageBitmap(Bitmap.createScaledBitmap(bitmap,
THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, false));
mAttachImagePreview.setVisibility(View.VISIBLE);
if (mListener != null) {
mListener.onMediaAttach();
}
} catch (OutOfMemoryError e) {
Log.e(ComposeTweetFragment.class.getName(), "out of memory when adding image",
e);
}
}
}
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth,
int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
private static int exifToDegrees(final int exifOrientation) {
int degrees = 0;
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
degrees = 90;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
degrees = 180;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
degrees = 270;
}
return degrees;
}
/*
*
*/
public void beginQuote(TwitterStatus statusToQuote) {
String statusString = getDefaultQuoteStatus(statusToQuote);
setComposeTweetDefault(new ComposeTweetDefault(getApp().getCurrentAccountScreenName(), statusString));
showCompose(statusString);
}
/*
*
*/
public void beginShare(String initialShareString) {
setComposeTweetDefault(new ComposeTweetDefault(getApp().getCurrentAccountScreenName(), initialShareString));
showCompose(initialShareString);
}
/*
*
*/
private TwitterStatus mRetweetStatus;
public void retweetSelected(TwitterStatus status, final FinishedCallback callback, final FinishedCallback showRTCallback) {
mRetweetStatus = status;
AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
.create();
alertDialog.setMessage(getString(R.string.alert_retweet_options));
// TODO: The order these buttons are set looks wrong, but appears
// correctly. Have to ensure this is consistent on other devices.
alertDialog.setButton2(getString(R.string.cancel),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
callback.finished(new TwitterFetchResult(true, "CancelPressed"), null);
}
});
alertDialog.setButton3(getString(R.string.quote),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (mRetweetStatus != null) {
callback.finished(new TwitterFetchResult(true, "QutotePressed"), null);
beginQuote(mRetweetStatus);
mRetweetStatus = null;
}
}
});
alertDialog.setButton(getString(R.string.retweet),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (mRetweetStatus != null) {
TwitterManager.get().setRetweet(mRetweetStatus.mId,
callback);
mRetweetStatus = null;
}
showRTCallback.finished(new TwitterFetchResult(true, "Show"), null);
}
});
alertDialog.show();
}
/*
* (non-Javadoc)
*
* @see
* com.tweetlanes.android.core.view.ComposeBaseFragment#getLayoutResourceId()
*/
@Override
protected int getLayoutResourceId() {
return R.layout.compose_tweet;
}
/*
* (non-Javadoc)
*
* @see com.tweetlanes.android.core.view.ComposeBaseFragment#onShowCompose()
*/
@Override
protected void onShowCompose() {
}
/*
* (non-Javadoc)
*
* @see com.tweetlanes.android.core.view.ComposeBaseFragment#onHideCompose()
*/
@Override
protected void onHideCompose() {
}
}