package com.orgzly.android.ui; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.TaskStackBuilder; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; import com.orgzly.BuildConfig; import com.orgzly.R; import com.orgzly.android.Book; import com.orgzly.android.Note; import com.orgzly.android.NotesBatch; import com.orgzly.android.Shelf; import com.orgzly.android.prefs.AppPreferences; import com.orgzly.android.ui.fragments.NoteFragment; import com.orgzly.android.ui.fragments.SyncFragment; import com.orgzly.android.ui.util.ActivityUtils; import com.orgzly.android.util.LogUtils; import com.orgzly.android.util.MiscUtils; import com.orgzly.org.datetime.OrgDateTime; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Set; /** * Activity started when shared to Orgzly. * * TODO: Resuming - intent will stay the same. * If activity is not finished (by save, cancel or pressing back), next share will resume the * activity and the intent will stay the same. Other apps seem to have the same problem and * it's not a common scenario, but it should be fixed. */ public class ShareActivity extends CommonActivity implements NoteFragment.NoteFragmentListener, SyncFragment.SyncFragmentListener { public static final String TAG = ShareActivity.class.getName(); /** Shared text files are read and their content is stored as note content. */ private static final long MAX_TEXT_FILE_LENGTH_FOR_CONTENT = 1024 * 1024 * 2; // 2 MB private static final String SPINNER_POSITION_KEY = "position"; private SyncFragment mSyncFragment; private NoteFragment mNoteFragment; private Spinner mBooksSpinner; private String mError; public static PendingIntent createNewNoteIntent(Context context) { Intent resultIntent = new Intent(context, ShareActivity.class); resultIntent.setAction(Intent.ACTION_SEND); resultIntent.setType("text/plain"); resultIntent.putExtra(Intent.EXTRA_TEXT, ""); // The stack builder object will contain an artificial back stack for the // started Activity. // This ensures that navigating backward from the Activity leads out of // your application to the Home screen. TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); // Adds the back stack for the Intent (but not the Intent itself) stackBuilder.addParentStack(ShareActivity.class); // Adds the Intent that starts the Activity to the top of the stack stackBuilder.addNextIntent(resultIntent); return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, savedInstanceState); setContentView(R.layout.activity_share); getSupportActionBar().setTitle(R.string.new_note); /* Set status and action bar colors depending on the fragment. */ ActivityUtils.setColorsForFragment(this, null); Data data = getDataFromIntent(getIntent()); setupFragments(savedInstanceState, data); setupBooksSpinner(savedInstanceState); setupBooksSpinnerAdapter(savedInstanceState); } public Data getDataFromIntent(Intent intent) { Data data = new Data(); mError = null; String action = intent.getAction(); String type = intent.getType(); if (action == null) { // mError = getString(R.string.share_action_not_set); } else if (type == null) { // mError = getString(R.string.share_type_not_set); } else if (action.equals(Intent.ACTION_SEND)) { if (type.startsWith("text/")) { if (intent.hasExtra(Intent.EXTRA_TEXT)) { data.title = intent.getStringExtra(Intent.EXTRA_TEXT); } else if (intent.hasExtra(Intent.EXTRA_STREAM)) { Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); data.title = uri.getLastPathSegment(); /* * Store file's content as note content. */ try { File file = new File(uri.getPath()); /* Don't read large files. */ if (file.length() > MAX_TEXT_FILE_LENGTH_FOR_CONTENT) { mError = "File has " + file.length() + " bytes (refusing to read files larger then " + MAX_TEXT_FILE_LENGTH_FOR_CONTENT + " bytes)"; } else { data.content = MiscUtils.readStringFromFile(file); } } catch (IOException e) { e.printStackTrace(); mError = "Failed reading the content of " + uri.toString() + ": " + e.toString(); } } if (data.title != null && data.content == null && intent.hasExtra(Intent.EXTRA_SUBJECT)) { data.content = data.title; data.title = intent.getStringExtra(Intent.EXTRA_SUBJECT); } } else { mError = getString(R.string.share_type_not_supported, type); } } else if (action.equals("com.google.android.gm.action.AUTO_SEND")) { if (type.startsWith("text/") && intent.hasExtra(Intent.EXTRA_TEXT)) { data.title = intent.getStringExtra(Intent.EXTRA_TEXT); } } else { mError = getString(R.string.share_action_not_supported, action); } /* Make sure that title is never empty. */ if (data.title == null) { data.title = ""; } return data; } private void setupBooksSpinnerAdapter(final Bundle savedInstanceState) { new AsyncTask<Void, Void, List<Book>>() { @Override protected List<Book> doInBackground(Void... params) { return getBooksList(); } @Override protected void onPostExecute(List<Book> books) { ArrayAdapter<Book> adapter = new ArrayAdapter<>(ShareActivity.this, R.layout.spinner_item, books); adapter.setDropDownViewResource(R.layout.dropdown_item); mBooksSpinner.setAdapter(adapter); if (savedInstanceState != null && savedInstanceState.containsKey(SPINNER_POSITION_KEY)) { mBooksSpinner.setSelection(savedInstanceState.getInt(SPINNER_POSITION_KEY, 0)); } else { String defaultBook = AppPreferences.shareNotebook(getApplicationContext()); for (int i=0; i<books.size(); i++) { if (defaultBook.equals(books.get(i).getName())) { mBooksSpinner.setSelection(i); break; } } } } }.execute(); } private void setupFragments(Bundle savedInstanceState, Data data) { /* Setup fragments. */ if (savedInstanceState == null) { /* Create and add fragments. */ mSyncFragment = SyncFragment.getInstance(); getSupportFragmentManager() .beginTransaction() .add(mSyncFragment, SyncFragment.FRAGMENT_TAG) .commit(); mNoteFragment = NoteFragment.getInstance(true, 0, 0, Place.UNDEFINED, data.title, data.content); getSupportFragmentManager() .beginTransaction() .replace(R.id.activity_share_main, mNoteFragment, NoteFragment.FRAGMENT_TAG) .commit(); } else { /* Get existing fragments. */ mSyncFragment = (SyncFragment) getSupportFragmentManager().findFragmentByTag(SyncFragment.FRAGMENT_TAG); mNoteFragment = (NoteFragment) getSupportFragmentManager().findFragmentByTag(NoteFragment.FRAGMENT_TAG); } } private void setupBooksSpinner(Bundle savedInstanceState) { if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, savedInstanceState); mBooksSpinner = (Spinner) findViewById(R.id.activity_share_books_spinner); /* On spinner book select - update note's book. */ mBooksSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, mBooksSpinner.getSelectedItem(), mNoteFragment); if (mBooksSpinner.getSelectedItem() != null) { Book book = (Book) mBooksSpinner.getSelectedItem(); if (book != null && mNoteFragment != null) { if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, "Setting book for fragment", book); mNoteFragment.setBook(book); } } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); } @Override protected void onResume() { super.onResume(); if (mError != null) { showSimpleSnackbarLong(mError); mError = null; } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mBooksSpinner != null && mBooksSpinner.getSelectedItem() != null) { outState.putInt(SPINNER_POSITION_KEY, mBooksSpinner.getSelectedItemPosition()); } if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, outState); } /** * Collects list of books from database. * If there are no books available, create one. */ private List<Book> getBooksList() { Shelf shelf = new Shelf(this); List<Book> books = shelf.getBooks(); if (books.size() == 0) { try { Book book = shelf.createBook(AppPreferences.shareNotebook(getApplicationContext())); books.add(book); } catch (IOException e) { // TODO: Test and handle better. e.printStackTrace(); finish(); } } return books; } @Override public void announceChanges(String fragmentTag, CharSequence title, CharSequence subTitle, int selectionCount) { } @Override public void onNoteCreateRequest(Note note, NotePlace notePlace) { mSyncFragment.createNote(note, notePlace); } @Override public void onNoteUpdateRequest(Note note) { } @Override public void onNoteCancelRequest(Note note) { finish(); } @Override public void onNoteDeleteRequest(Note note) { } @Override public void onBookCreated(Book book) { } @Override public void onBookCreationFailed(Exception exception) { } @Override public void onBookLoaded(Book book) { } @Override public void onBookLoadFailed(Exception exception) { } @Override public void onBookSaved(Book book) { } @Override public void onBookForceSavingFailed(Exception exception) { } @Override public void onSyncFinished(String msg) { } @Override public void onBookExported(File file) { } @Override public void onBookExportFailed(Exception exception) { } @Override public void onNotesPasted(NotesBatch batch) { } @Override public void onNotesNotPasted() { } @Override public void onDatabaseCleared() { } @Override public void onBookDeleted(Book book) { } @Override public void onBookDeletingFailed(Book book, IOException exception) { } @Override public void onScheduledTimeUpdated(Set<Long> noteIds, OrgDateTime time) { } @Override public void onStateChanged(Set<Long> noteIds, String state) { } @Override public void onNoteCreated(Note note) { finish(); } @Override public void onNoteCreatingFailed() { } @Override public void onNoteUpdated(Note note) { } @Override public void onNoteUpdatingFailed(Note note) { } @Override public void onNotesDeleted(int count) { } @Override public void onNotesCut(int count) { } private class Data { String title; String content; } }