package info.guardianproject.securereaderinterface;
import info.guardianproject.securereader.SocialReader;
import info.guardianproject.securereader.XMLRPCPublisher.XMLRPCPublisherCallback;
import info.guardianproject.securereaderinterface.ui.MediaViewCollection;
import info.guardianproject.securereaderinterface.ui.UICallbacks;
import info.guardianproject.securereaderinterface.uiutil.AnimationHelpers;
import info.guardianproject.securereaderinterface.uiutil.AnimationHelpers.FadeInFadeOutListener;
import info.guardianproject.securereaderinterface.uiutil.UIHelpers;
import info.guardianproject.securereaderinterface.views.CreateAccountView;
import info.guardianproject.securereaderinterface.views.PostSignInView;
import info.guardianproject.securereaderinterface.views.StoryMediaContentView;
import info.guardianproject.securereaderinterface.views.CreateAccountView.OnActionListener;
import info.guardianproject.securereaderinterface.views.PostSignInView.OnAgreeListener;
import info.guardianproject.securereaderinterface.R;
import info.guardianproject.iocipher.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.view.ActionProvider;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.tinymission.rss.Item;
import com.tinymission.rss.MediaContent;
public class AddPostActivity extends FragmentActivityWithMenu implements OnActionListener, OnFocusChangeListener, OnAgreeListener,
FadeInFadeOutListener
{
ProgressDialog loadingDialog;
private static final int REQ_CODE_PICK_IMAGE = 1;
private StoryMediaContentView mMediaView;
private EditText mEditTitle;
private EditText mEditContent;
private EditText mEditTags;
private Item mStory;
private CreateAccountView mViewCreateAccount;
private PostSignInView mViewSignIn;
private View mBtnMediaAdd;
private View mBtnMediaAddMore;
private View mBtnMediaReplace;
private View mBtnMediaView;
private View mBtnMediaDelete;
private View mOperationButtons;
private View mProgressIcon;
private java.io.File mCurrentPhotoFile;
private int mReplaceThisIndex;
private boolean mIsAddingMedia;
private AlertDialog mMediaChooserDialog;
private Intent mStartedIntent;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_post);
// Display home as up
setDisplayHomeAsUp(true);
setMenuIdentifier(R.menu.activity_add_post);
mReplaceThisIndex = -1;
mIsAddingMedia = false;
}
@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
this.setContentView(R.layout.activity_add_post);
}
@Override
public void onContentChanged()
{
super.onContentChanged();
mEditTitle = (EditText) findViewById(R.id.editTitle);
mEditContent = (EditText) findViewById(R.id.editContent);
mEditTags = (EditText) findViewById(R.id.editTags);
mMediaView = (StoryMediaContentView) findViewById(R.id.mediaContentView);
mMediaView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
if (!mIsAddingMedia && mOperationButtons.getVisibility() == View.GONE)
{
mOperationButtons.setVisibility(View.VISIBLE);
AnimationHelpers.fadeIn(mOperationButtons, 500, 5000, false);
}
}
});
mMediaView = (StoryMediaContentView) findViewById(R.id.mediaContentView);
mMediaView.setShowPlaceholder(true);
mMediaView.setShowPlaceholderWhileLoading(true);
mOperationButtons = findViewById(R.id.llOperationButtons);
mOperationButtons.setVisibility(View.GONE);
AnimationHelpers.fadeOut(mOperationButtons, 0, 0, false);
hookupMediaOperationButtons();
// If this is "edit" and not "add" the edited story is sent in the
// intent. Get it!
if (getIntent().hasExtra("story"))
{
long storyId = getIntent().getLongExtra("story", 0);
for (Item story : App.getInstance().socialReporter.getDrafts())
{
if (story.getDatabaseId() == storyId)
{
mStory = story;
break;
}
}
}
// Hacking this in...
// http://developer.android.com/training/sharing/receive.html
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null)
{
if ("text/plain".equals(type))
{
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
mEditContent.setText(sharedText);
}
else if (type.startsWith("image/"))
{
Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
// addMediaItem(imageUri, type, -1);
}
else if (type.startsWith("video/"))
{
Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
// addMediaItem(imageUri, type, -1);
}
}
// We need to react on the soft keyboard being shown, to make sure that
// the input fields are
// shown correctly. This is taken from:
// http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android/4737265#4737265
//
final View activityRootView = findViewById(R.id.llRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
Rect r = new Rect();
// r will be populated with the coordinates of your view
// that
// area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - r.height();
if (heightDiff > 100)
{ // if more than 100 pixels, its
// probably a keyboard...
final ScrollView sv = (ScrollView) findViewById(R.id.sv0);
if (sv != null)
{
sv.post(new Runnable()
{
@Override
public void run()
{
sv.scrollTo(0, sv.getBottom());
}
});
}
}
}
});
mViewSignIn = (PostSignInView) findViewById(R.id.signIn);
mViewSignIn.setActionListener(this);
mViewCreateAccount = (CreateAccountView) findViewById(R.id.createAccount);
mViewCreateAccount.setActionListener(this);
mEditTags.addTextChangedListener(new TagTextWatcher());
showHideCreateAccount(false);
// Sat focus change listener so we can auto save draft on lost focus
// event
mEditTitle.setOnFocusChangeListener(this);
mEditContent.setOnFocusChangeListener(this);
mEditTags.setOnFocusChangeListener(this);
populateFromStory();
updateMediaControls();
}
private void hookupMediaOperationButtons()
{
mProgressIcon = findViewById(R.id.ivProgressIcon);
mProgressIcon.setVisibility(View.GONE);
mBtnMediaAdd = findViewById(R.id.btnMediaAdd);
mBtnMediaAdd.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
createMediaChooser(-1);
}
});
mBtnMediaAddMore = findViewById(R.id.btnMediaAddMore);
mBtnMediaAddMore.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
createMediaChooser(-1);
}
});
mBtnMediaReplace = findViewById(R.id.btnMediaReplace);
mBtnMediaReplace.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
int currentIndex = mMediaView.getCurrentItemIndex();
createMediaChooser(currentIndex);
}
});
mBtnMediaView = findViewById(R.id.btnMediaView);
mBtnMediaView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
int currentIndex = mMediaView.getCurrentItemIndex();
if (mStory != null && currentIndex < mStory.getNumberOfMediaContent())
{
MediaContent mediaContent = mStory.getMediaContent().get(currentIndex);
Bundle mediaData = new Bundle();
mediaData.putSerializable("media", mediaContent);
UICallbacks.handleCommand(v.getContext(), R.integer.command_view_media, mediaData);
}
}
});
mBtnMediaDelete = findViewById(R.id.btnMediaDelete);
mBtnMediaDelete.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
int currentIndex = mMediaView.getCurrentItemIndex();
if (mStory != null && currentIndex < mStory.getNumberOfMediaContent())
{
mStory.getMediaContent().remove(currentIndex);
onMediaChanged();
}
}
});
}
private java.io.File getAlbumDir()
{
return new java.io.File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), App.getInstance().getString(R.string.app_name));
}
private void createImageFile(boolean isVideo) throws IOException
{
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = timeStamp + "_";
java.io.File dir = getAlbumDir();
dir.mkdir();
java.io.File image = File.createTempFile(imageFileName, isVideo ? ".mp4" : ".jpg", dir);
mCurrentPhotoFile = image;
}
private void deleteImageFile()
{
if (mCurrentPhotoFile != null)
{
mCurrentPhotoFile.delete();
mCurrentPhotoFile = null;
}
}
private void copyFileFromFStoAppFS(java.io.InputStream in, java.io.File src, info.guardianproject.iocipher.File dst) throws IOException
{
if (in == null)
in = new java.io.FileInputStream(src);
OutputStream out = new info.guardianproject.iocipher.FileOutputStream(dst);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0)
{
out.write(buf, 0, len);
// Log.v("WRITING", ""+len);
}
in.close();
out.close();
Log.v("copyFileFromFStoAppFS", "Copied from " + ((src == null) ? "stream" : src.toString()) + " to " + dst.toString());
}
private void updateMediaControls()
{
// Show or hide the add media button
if (mStory == null || mStory.getNumberOfMediaContent() == 0)
{
mMediaView.setVisibility(View.INVISIBLE);
mBtnMediaAdd.setVisibility(View.VISIBLE);
mOperationButtons.setVisibility(View.GONE);
}
else
{
mMediaView.setVisibility(View.VISIBLE);
mBtnMediaAdd.setVisibility(View.GONE);
mOperationButtons.setVisibility(View.GONE);
}
}
private void populateFromStory()
{
if (mStory == null)
return;
mEditTitle.setText(mStory.getTitle());
mEditContent.setText(mStory.getCleanMainContent());
ArrayList<String> tags = mStory.getTags();
if (tags != null)
{
StringBuilder sb = new StringBuilder();
for (String tag : tags)
{
if (sb.length() > 0)
sb.append(" ");
sb.append(tag);
}
mEditTags.setText(sb.toString());
}
MediaViewCollection collection = new MediaViewCollection(mMediaView.getContext(), mStory);
collection.load(true, false);
mMediaView.setMediaCollection(collection, false, false);
}
private ArrayList<String> getTagsFromInput()
{
ArrayList<String> ret = null;
String tagsString = mEditTags.getText().toString();
if (tagsString != null)
{
tagsString = tagsString.replace("#", ""); // remove # from the
// string
if (tagsString.length() > 0)
ret = new ArrayList<String>(Arrays.asList(tagsString.split("\\s+", 0)));
}
return ret;
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
if (mEditTitle.getText().length() > 0)
outState.putString("title", mEditTitle.getText().toString());
if (mEditContent.getText().length() > 0)
outState.putString("content", mEditContent.getText().toString());
if (mEditTags.getText().length() > 0)
outState.putString("tags", mEditTags.getText().toString());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.containsKey("title"))
mEditTitle.setText(savedInstanceState.getString("title"));
if (savedInstanceState.containsKey("content"))
mEditContent.setText(savedInstanceState.getString("content"));
if (savedInstanceState.containsKey("tags"))
mEditTags.setText(savedInstanceState.getString("tags"));
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
boolean ret = super.onCreateOptionsMenu(menu);
MenuItem menuPost = menu.findItem(R.id.menu_post);
if (menuPost != null)
{
menuPost.setActionProvider(new ActionProvider(this)
{
@Override
public View onCreateActionView()
{
LayoutInflater inflater = LayoutInflater.from(AddPostActivity.this);
View view = inflater.inflate(R.layout.actionbar_green_button, null);
view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
TextView tv = (TextView) view.findViewById(R.id.tvItem);
tv.setText(R.string.add_post_menu_post);
view.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
if (!checkValidPost())
{
showNoValidDataWarning();
return;
}
saveDraft(false);
loadingDialog = ProgressDialog.show(AddPostActivity.this, "", "Posting. Please wait...", true, true);
XMLRPCPublisherCallback publisherCallback = new XMLRPCPublisherCallback()
{
@Override
public void itemPublished(Item _item)
{
if (loadingDialog.isShowing())
{
loadingDialog.dismiss();
quitBackToList(1); // "outbox"
}
}
};
App.getInstance().socialReporter.publish(mStory, publisherCallback);
}
});
return view;
}
@Override
public boolean hasSubMenu()
{
return false;
}
});
}
return ret;
}
private boolean saveDraftOrAskForDeletion()
{
// Does it contain data at the moment?
if (isEmpty() && (mStory == null || mStory.getNumberOfMediaContent() == 0))
{
// No. If we have saved a draft already, ask if user wants to
// delete it
if (mStory != null)
{
Builder alert = new AlertDialog.Builder(this).setPositiveButton(R.string.add_post_draft_delete, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
App.getInstance().socialReporter.deleteDraft(mStory);
quitBackToList(-1); // wherever we were
// before
}
}).setNegativeButton(R.string.add_post_draft_save, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
if (saveDraft(false))
Toast.makeText(AddPostActivity.this, R.string.add_post_draft_saved, Toast.LENGTH_SHORT).show();
dialog.dismiss();
quitBackToList(2); // drafts
}
}).setMessage(R.string.add_post_draft_delete_empty);
alert.show();
return false;
}
}
if (saveDraft(false))
Toast.makeText(this, R.string.add_post_draft_saved, Toast.LENGTH_SHORT).show();
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case android.R.id.home:
if (this.saveDraftOrAskForDeletion())
quitBackToList(2); // drafts
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onUnlockedActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent)
{
super.onUnlockedActivityResult(requestCode, resultCode, imageReturnedIntent);
switch (requestCode)
{
case REQ_CODE_PICK_IMAGE:
if (resultCode == RESULT_OK)
{
try
{
String defaultType = "image/jpeg";
if (mStartedIntent.getAction().equals(MediaStore.ACTION_IMAGE_CAPTURE))
{
this.addMediaItem(null, mCurrentPhotoFile, null, defaultType, mReplaceThisIndex);
}
else if (mStartedIntent.getAction().equals(MediaStore.ACTION_VIDEO_CAPTURE))
{
defaultType = "video/mp4";
this.addMediaItem(null, mCurrentPhotoFile, null, defaultType, mReplaceThisIndex);
}
else
{
java.io.File mediaItemFile = null;
java.io.InputStream mediaItemStream = null;
String[] projections = new String[] { android.provider.MediaStore.Images.ImageColumns.DATA,
android.provider.MediaStore.Images.ImageColumns.MIME_TYPE };
Cursor cursor = getContentResolver().query(imageReturnedIntent.getData(), projections, null, null, null);
if (cursor != null)
{
if (cursor.moveToFirst())
{
int idxData = cursor.getColumnIndexOrThrow(android.provider.MediaStore.Images.ImageColumns.DATA);
int idxMime = cursor.getColumnIndexOrThrow(android.provider.MediaStore.Images.ImageColumns.MIME_TYPE);
String path = cursor.getString(idxData);
if (path != null)
{
mediaItemFile = new java.io.File(path);
}
String mediaType = cursor.getString(idxMime);
if (mediaType != null)
{
defaultType = mediaType;
}
}
cursor.close();
}
if (mediaItemFile == null)
{
Uri uriMedia = imageReturnedIntent.getData();
try
{
mediaItemStream = getContentResolver().openInputStream(uriMedia);
}
catch (Exception ex)
{
mediaItemStream = null;
}
}
this.addMediaItem(mediaItemStream, mediaItemFile, imageReturnedIntent.getData().toString(), defaultType, mReplaceThisIndex);
}
}
catch (Exception ex)
{
Log.e(MainActivity.LOGTAG, "Failed to add image/video: " + ex.toString());
}
}
else
{
// Delete temp file, if we created one
deleteImageFile();
}
}
}
/**
* Add or replace a piece of media in the current draft.
*
* @param mediaItem
* Uri to the new media to add
* @param defaultType
* If type can't be decided automatically, use this as a default
* type
* @param replaceIndex
* Optional index to replace (-1 for normal add)
*/
// private void addMediaItem(Uri mediaItem, String defaultType, int
// replaceIndex)
// We want files here so we can deal with them all the same
private void addMediaItem(java.io.InputStream mediaItemStream, java.io.File mediaItemFile, String mediaItemUrl, String mediaType, int replaceIndex)
{
if (mStory == null)
saveDraft(true);
MediaContent newMediaContent = new MediaContent(mStory.getDatabaseId(), "", mediaType);
// Let's get a record with an id
if (replaceIndex == -1)
{
mStory.addMediaContent(newMediaContent);
}
else
{
ArrayList<MediaContent> rgMedia = mStory.getMediaContent();
rgMedia.remove(replaceIndex);
rgMedia.add(replaceIndex, newMediaContent);
}
// This should give us a database id if one doesn't exist already
saveDraft(false);
mProgressIcon.startAnimation(AnimationUtils.loadAnimation(this, R.anim.rotate));
mProgressIcon.setVisibility(View.VISIBLE);
mBtnMediaAdd.setVisibility(View.GONE);
mIsAddingMedia = true;
ThreadedTask<Void, Void, Void> addMediaTask = new ThreadedTask<Void, Void, Void>()
{
private InputStream mediaItemStream;
private java.io.File mediaItemFile;
private String mediaItemUrl;
private MediaContent mediaContent;
public ThreadedTask<Void, Void, Void> init(java.io.InputStream mediaItemStream, java.io.File mediaItemFile, String mediaItemUrl, MediaContent mediaContent)
{
this.mediaItemStream = mediaItemStream;
this.mediaItemFile = mediaItemFile;
this.mediaItemUrl = mediaItemUrl;
this.mediaContent = mediaContent;
return this;
}
@Override
protected Void doInBackground(Void... values)
{
if (mediaItemStream != null || mediaItemFile != null)
{
// Now we can copy the file
File outputFile;
outputFile = new File(App.getInstance().socialReader.getFileSystemDir(), SocialReader.MEDIA_CONTENT_FILE_PREFIX + mediaContent.getDatabaseId());
Log.v("AddPostActivity", "Local App Storage File: " + outputFile);
// First copy file to encrypted storage
try
{
copyFileFromFStoAppFS(mediaItemStream, mediaItemFile, outputFile);
}
catch (IOException e)
{
e.printStackTrace();
}
// Update record in media content
mediaContent.setUrl("file://" + outputFile.getAbsolutePath());
}
else
{
mediaContent.setUrl(mediaItemUrl);
}
Log.v("AddPostActivity", "Set Url to: " + mediaContent.getUrl());
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mIsAddingMedia = false;
mProgressIcon.clearAnimation();
mProgressIcon.setVisibility(View.GONE);
// If this was a temp capture file, delete it
deleteImageFile();
onMediaChanged();
}
}.init(mediaItemStream, mediaItemFile, mediaItemUrl, newMediaContent);
addMediaTask.execute((Void)null);
}
private void onMediaChanged()
{
// Save our changes
saveDraft(false);
// Update the media view to show new media as well
MediaViewCollection collection = new MediaViewCollection(mMediaView.getContext(), mStory);
collection.load(true, false);
mMediaView.setMediaCollection(collection, false, false);
updateMediaControls();
}
private boolean isEmpty()
{
if (TextUtils.isEmpty(mEditTitle.getText()) && TextUtils.isEmpty(mEditContent.getText()))
{
ArrayList<String> tags = getTagsFromInput();
if (tags == null || tags.size() == 0)
return true;
}
return false;
}
private boolean saveDraft(boolean forceCreate)
{
Log.v("AddPostActivity", "Saving draft");
if (mStory == null)
{
// If nothing had been changed yet, no need to save!
if (!forceCreate && TextUtils.isEmpty(mEditTitle.getText()) && TextUtils.isEmpty(mEditContent.getText()) && TextUtils.isEmpty(mEditTags.getText()))
return false;
mStory = App.getInstance().socialReporter.createDraft(mEditTitle.getText().toString(), mEditContent.getText().toString(), getTagsFromInput(), null);
getIntent().putExtra("story", mStory.getDatabaseId());
}
else
{
mStory.setTitle(mEditTitle.getText().toString());
mStory.setDescription(mEditContent.getText().toString());
mStory.getTags().clear();
ArrayList<String> tags = getTagsFromInput();
if (tags != null && tags.size() > 0)
mStory.getTags().addAll(tags);
}
App.getInstance().socialReporter.saveDraft(mStory);
Log.v("SaveDraft", "Story Database Id: " + mStory.getDatabaseId());
return true;
}
private boolean checkValidPost()
{
if (TextUtils.isEmpty(mEditTitle.getText()))
return false;
return true;
}
private void showNoValidDataWarning()
{
Builder alert = new AlertDialog.Builder(this).setTitle(R.string.add_post_not_valid_data_warning_title)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
}).setMessage(R.string.add_post_not_valid_data_warning_content);
alert.show();
}
private class TagTextWatcher implements TextWatcher
{
private boolean mRemoved;
private boolean mChanging;
private final int mHashColor;
public TagTextWatcher()
{
mHashColor = getResources().getColor(R.color.grey_light_medium);
}
@Override
public void afterTextChanged(Editable s)
{
if (!mChanging)
{
if (mRemoved)
{
if (s.length() > 0)
{
int remove = 0;
int idx = s.length() - 1;
while (Character.isWhitespace(s.charAt(idx)))
{
remove++;
idx--;
}
if (remove > 0)
{
mChanging = true;
s.delete(s.length() - remove, s.length());
mChanging = false;
}
}
}
else
{
if (s.length() == 0 || Character.isWhitespace(s.charAt(s.length() - 1)))
{
mChanging = true;
s.append("#");
mChanging = false;
}
else if (s.length() > 0 && s.charAt(0) != '#')
{
mChanging = true;
s.insert(0, "#");
mChanging = false;
}
}
mChanging = true;
// Simply doing s.clearSpans does not work, see
// http://stackoverflow.com/questions/9403057/android-edittext-clearing-spans
ForegroundColorSpan[] toRemoveSpans = s.getSpans(0, s.length(), ForegroundColorSpan.class);
for (int i = 0; i < toRemoveSpans.length; i++)
s.removeSpan(toRemoveSpans[i]);
// Style the hashes in a more discrete grey color
for (int i = s.length() - 1; i >= 0; i--)
{
if (s.charAt(i) == '#')
{
s.setSpan(new ForegroundColorSpan(mHashColor), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Make sure a space exists before hashes
if (i > 0 && !Character.isWhitespace(s.charAt(i - 1)))
{
s.insert(i, " ");
}
}
}
mChanging = false;
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
if (count < before)
mRemoved = true;
else
mRemoved = false;
}
}
private void quitBackToList(int go_to_tab)
{
Bundle commandParameters = null;
if (go_to_tab != -1)
{
commandParameters = new Bundle();
commandParameters.putInt("go_to_tab", go_to_tab);
}
UICallbacks.handleCommand(this, R.integer.command_posts_list, commandParameters);
overridePendingTransition(R.anim.slide_in_from_left, R.anim.slide_out_to_right);
finish();
}
@Override
public void onBackPressed()
{
if (saveDraftOrAskForDeletion())
quitBackToList(2); // drafts
}
@Override
public void onFocusChange(View v, boolean hasFocus)
{
saveDraft(false);
}
private class HandlerIntent
{
public final Intent intent;
public final ResolveInfo resolveInfo;
public HandlerIntent(Intent intent, ResolveInfo resolveInfo)
{
this.intent = intent;
this.resolveInfo = resolveInfo;
}
}
private class HandlerIntentListAdapter extends ArrayAdapter<HandlerIntent>
{
public HandlerIntentListAdapter(Context context, HandlerIntent[] intents)
{
super(context, android.R.layout.select_dialog_item, android.R.id.text1, intents);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// User super class to create the View
View v = super.getView(position, convertView, parent);
TextView tv = (TextView) v.findViewById(android.R.id.text1);
HandlerIntent handlerIntent = getItem(position);
ResolveInfo info = handlerIntent.resolveInfo;
PackageManager pm = getContext().getPackageManager();
tv.setText(info.loadLabel(pm));
Drawable icon = info.loadIcon(pm);
int iconSize = UIHelpers.dpToPx(32, getContext());
icon.setBounds(0, 0, iconSize, iconSize);
// Put the image on the TextView
tv.setCompoundDrawables(icon, null, null, null);
tv.setCompoundDrawablePadding(UIHelpers.dpToPx(10, getContext()));
return v;
}
};
private void getHandlersForIntent(Intent intent, ArrayList<HandlerIntent> rgIntents)
{
PackageManager pm = getPackageManager();
List<ResolveInfo> resInfos = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resInfo : resInfos)
{
rgIntents.add(new HandlerIntent(intent, resInfo));
}
}
private void createMediaChooser(int replaceThisIndex)
{
mReplaceThisIndex = replaceThisIndex; // Remember which index to
// replace, if any (set to -1
// for "add")
Builder alert = new AlertDialog.Builder(this);
LayoutInflater inflater = LayoutInflater.from(this);
View contentView = inflater.inflate(R.layout.add_post_media_chooser, null);
TabHost tabHost = (TabHost) contentView.findViewById(android.R.id.tabhost);
tabHost.setup();
TabSpec spec = tabHost.newTabSpec("photo");
View tabView = inflater.inflate(R.layout.light_tab_item, null);
((TextView) tabView.findViewById(R.id.tvItem)).setText(getString(R.string.add_post_media_chooser_photo));
spec.setIndicator(tabView);
spec.setContent(R.id.lvPhoto);
tabHost.addTab(spec);
ListView lv = (ListView) contentView.findViewById(R.id.lvPhoto);
// Populate photo intents
final ArrayList<HandlerIntent> rgIntentsPhoto = new ArrayList<HandlerIntent>();
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
getHandlersForIntent(cameraIntent, rgIntentsPhoto);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
getHandlersForIntent(intent, rgIntentsPhoto);
ListAdapter adapterPhoto = new HandlerIntentListAdapter(this, rgIntentsPhoto.toArray(new HandlerIntent[rgIntentsPhoto.size()]));
lv.setAdapter(adapterPhoto);
lv.setOnItemClickListener(new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
startResolvedIntent((HandlerIntent) parent.getAdapter().getItem(position));
}
});
spec = tabHost.newTabSpec("video");
tabView = inflater.inflate(R.layout.light_tab_item, null);
((TextView) tabView.findViewById(R.id.tvItem)).setText(getString(R.string.add_post_media_chooser_video));
spec.setIndicator(tabView);
spec.setContent(R.id.lvVideo);
tabHost.addTab(spec);
// Populate video intents
lv = (ListView) contentView.findViewById(R.id.lvVideo);
lv.setOnItemClickListener(new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
startResolvedIntent((HandlerIntent) parent.getAdapter().getItem(position));
}
});
final ArrayList<HandlerIntent> rgIntentsVideo = new ArrayList<HandlerIntent>();
Intent cameraIntentVideo = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
getHandlersForIntent(cameraIntentVideo, rgIntentsVideo);
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("video/*");
getHandlersForIntent(intent, rgIntentsVideo);
ListAdapter adapterVideo = new HandlerIntentListAdapter(this, rgIntentsVideo.toArray(new HandlerIntent[rgIntentsVideo.size()]));
lv.setAdapter(adapterVideo);
alert.setView(contentView);
mMediaChooserDialog = alert.show();
}
protected void startResolvedIntent(HandlerIntent info)
{
try
{
if (MediaStore.ACTION_IMAGE_CAPTURE.equals(info.intent.getAction()) || MediaStore.ACTION_VIDEO_CAPTURE.equals(info.intent.getAction()))
{
boolean bIsVideo = MediaStore.ACTION_VIDEO_CAPTURE.equals(info.intent.getAction());
createImageFile(bIsVideo);
Uri uriSavedImage = Uri.fromFile(mCurrentPhotoFile.getAbsoluteFile());
info.intent.putExtra(MediaStore.EXTRA_OUTPUT, uriSavedImage);
}
info.intent.setClassName(info.resolveInfo.activityInfo.packageName, info.resolveInfo.activityInfo.name);
mStartedIntent = info.intent;
startActivityForResult(info.intent, REQ_CODE_PICK_IMAGE);
mMediaChooserDialog.dismiss();
}
catch (IOException e)
{
e.printStackTrace();
deleteImageFile();
}
}
private void showHideCreateAccount(boolean animate)
{
if (App.getSettings().acceptedPostPermission())
{
mViewCreateAccount.setVisibility(View.GONE);
if (animate)
AnimationHelpers.fadeOut(mViewSignIn, 500, 0, false, this);
else
mViewSignIn.setVisibility(View.GONE);
}
else if (App.getInstance().socialReporter.getAuthorName() != null)
{
if (animate)
{
AnimationHelpers.fadeOut(mViewCreateAccount, 500, 0, false, this);
AnimationHelpers.fadeIn(mViewSignIn, 500, 0, false, this);
}
else
{
mViewSignIn.setVisibility(View.VISIBLE);
mViewCreateAccount.setVisibility(View.GONE);
}
}
else
{
mViewCreateAccount.setVisibility(View.VISIBLE);
}
}
@Override
public void onCreateIdentity(String authorName)
{
App.getInstance().socialReporter.createAuthorName(authorName);
showHideCreateAccount(true);
}
@Override
public void onAgreed()
{
App.getSettings().setAcceptedPostPermission(true);
showHideCreateAccount(true);
}
@Override
protected void onWipe()
{
super.onWipe();
quitBackToList(0); // published
}
@Override
public void onFadeInStarted(View view)
{
if (view == mViewSignIn)
view.setVisibility(View.VISIBLE);
}
@Override
public void onFadeInEnded(View view)
{
}
@Override
public void onFadeOutStarted(View view)
{
}
@Override
public void onFadeOutEnded(View view)
{
view.setVisibility(View.GONE);
// To avoid old device bug, see
// http://stackoverflow.com/questions/4728908/android-view-with-view-gone-still-receives-ontouch-and-onclick
view.clearAnimation();
}
}