package it.niedermann.owncloud.notes.edit; import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.FAVORITES; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.preference.PreferenceManager; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; import com.nextcloud.android.sso.helper.SingleAccountHelper; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Calendar; import java.util.Objects; import it.niedermann.android.sharedpreferences.SharedPreferenceBooleanLiveData; import it.niedermann.owncloud.notes.LockedActivity; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.accountpicker.AccountPickerListener; import it.niedermann.owncloud.notes.databinding.ActivityEditBinding; import it.niedermann.owncloud.notes.edit.category.CategoryViewModel; import it.niedermann.owncloud.notes.main.MainActivity; import it.niedermann.owncloud.notes.persistence.entity.Account; import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.shared.model.NavigationCategory; import it.niedermann.owncloud.notes.shared.util.NoteUtil; import it.niedermann.owncloud.notes.shared.util.ShareUtil; public class EditNoteActivity extends LockedActivity implements BaseNoteFragment.NoteFragmentListener, AccountPickerListener { private static final String TAG = EditNoteActivity.class.getSimpleName(); public static final String ACTION_SHORTCUT = "it.niedermann.owncloud.notes.shortcut"; private static final String INTENT_GOOGLE_ASSISTANT = "com.google.android.gm.action.AUTO_SEND"; private static final String MIMETYPE_TEXT_PLAIN = "text/plain"; public static final String PARAM_NOTE_ID = "noteId"; public static final String PARAM_ACCOUNT_ID = "accountId"; public static final String PARAM_CATEGORY = "category"; public static final String PARAM_CONTENT = "content"; public static final String PARAM_FAVORITE = "favorite"; private CategoryViewModel categoryViewModel; private ActivityEditBinding binding; private BaseNoteFragment fragment; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { if (SingleAccountHelper.getCurrentSingleSignOnAccount(this) == null) { throw new NoCurrentAccountSelectedException(); } } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { Toast.makeText(this, R.string.no_account_configured_yet, Toast.LENGTH_LONG).show(); finish(); return; } final var preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); new SharedPreferenceBooleanLiveData(preferences, getString(R.string.pref_key_keep_screen_on), true).observe(this, keepScreenOn -> { if (keepScreenOn) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } }); categoryViewModel = new ViewModelProvider(this).get(CategoryViewModel.class); binding = ActivityEditBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); if (savedInstanceState == null) { launchNoteFragment(); } else { fragment = (BaseNoteFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container_view); } setSupportActionBar(binding.toolbar); binding.toolbar.setOnClickListener((v) -> fragment.showEditTitleDialog()); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.d(TAG, "onNewIntent: " + intent.getLongExtra(PARAM_NOTE_ID, 0)); setIntent(intent); if (fragment != null) { getSupportFragmentManager().beginTransaction().detach(fragment).commit(); fragment = null; } launchNoteFragment(); } @Override protected void onStop() { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); super.onStop(); } private long getNoteId() { return getIntent().getLongExtra(PARAM_NOTE_ID, 0); } private long getAccountId() { return getIntent().getLongExtra(PARAM_ACCOUNT_ID, 0); } /** * Starts the note fragment for an existing note or a new note. * The actual behavior is triggered by the activity's intent. */ private void launchNoteFragment() { long noteId = getNoteId(); if (noteId > 0) { launchExistingNote(getAccountId(), noteId); } else { if (Intent.ACTION_VIEW.equals(getIntent().getAction())) { launchReadonlyNote(); } else { launchNewNote(); } } } /** * Starts a {@link NoteEditFragment} or {@link NotePreviewFragment} for an existing note. * The type of fragment (view-mode) is chosen based on the user preferences. * * @param noteId ID of the existing note. */ private void launchExistingNote(long accountId, long noteId) { final var prefKeyNoteMode = getString(R.string.pref_key_note_mode); final var prefKeyLastMode = getString(R.string.pref_key_last_note_mode); final var prefValueEdit = getString(R.string.pref_value_mode_edit); final var prefValuePreview = getString(R.string.pref_value_mode_preview); final var prefValueLast = getString(R.string.pref_value_mode_last); final var preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); final String mode = preferences.getString(prefKeyNoteMode, prefValueEdit); final String lastMode = preferences.getString(prefKeyLastMode, prefValueEdit); boolean editMode = true; if (prefValuePreview.equals(mode) || (prefValueLast.equals(mode) && prefValuePreview.equals(lastMode))) { editMode = false; } launchExistingNote(accountId, noteId, editMode); } /** * Starts a {@link NoteEditFragment} or {@link NotePreviewFragment} for an existing note. * * @param noteId ID of the existing note. * @param edit View-mode of the fragment: * true for {@link NoteEditFragment}, * false for {@link NotePreviewFragment}. */ private void launchExistingNote(long accountId, long noteId, boolean edit) { // save state of the fragment in order to resume with the same note and originalNote Fragment.SavedState savedState = null; if (fragment != null) { savedState = getSupportFragmentManager().saveFragmentInstanceState(fragment); } fragment = edit ? NoteEditFragment.newInstance(accountId, noteId) : NotePreviewFragment.newInstance(accountId, noteId); if (savedState != null) { fragment.setInitialSavedState(savedState); } getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit(); } /** * Starts the {@link NoteEditFragment} with a new note. * Content ("share" functionality), category and favorite attribute can be preset. */ private void launchNewNote() { final var intent = getIntent(); String categoryTitle = ""; boolean favorite = false; if (intent.hasExtra(PARAM_CATEGORY)) { final NavigationCategory categoryPreselection = (NavigationCategory) Objects.requireNonNull(intent.getSerializableExtra(PARAM_CATEGORY)); final String category = categoryPreselection.getCategory(); if(category != null) { categoryTitle = category; } favorite = categoryPreselection.getType() == FAVORITES; } String content = ""; if ( intent.hasExtra(Intent.EXTRA_TEXT) && MIMETYPE_TEXT_PLAIN.equals(intent.getType()) && (Intent.ACTION_SEND.equals(intent.getAction()) || INTENT_GOOGLE_ASSISTANT.equals(intent.getAction())) ) { content = ShareUtil.extractSharedText(intent); } else if (intent.hasExtra(PARAM_CONTENT)) { content = intent.getStringExtra(PARAM_CONTENT); } if (content == null) { content = ""; } final var newNote = new Note(null, Calendar.getInstance(), NoteUtil.generateNonEmptyNoteTitle(content, this), content, categoryTitle, favorite, null); fragment = NoteEditFragment.newInstanceWithNewNote(newNote); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit(); } private void launchReadonlyNote() { final var intent = getIntent(); final var content = new StringBuilder(); try { final var inputStream = getContentResolver().openInputStream(Objects.requireNonNull(intent.getData())); final var bufferedReader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream))); String line; while ((line = bufferedReader.readLine()) != null) { content.append(line).append('\n'); } } catch (IOException e) { e.printStackTrace(); } fragment = NoteReadonlyFragment.newInstance(content.toString()); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit(); } @Override public void onBackPressed() { super.onBackPressed(); close(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_note_activity, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { final int itemId = item.getItemId(); if (itemId == android.R.id.home) { close(); return true; } else if (itemId == R.id.menu_preview) { launchExistingNote(getAccountId(), getNoteId(), false); return true; } else if (itemId == R.id.menu_edit) { launchExistingNote(getAccountId(), getNoteId(), true); return true; } return super.onOptionsItemSelected(item); } /** * Send result and closes the Activity */ public void close() { /* TODO enhancement: store last mode in note * for cross device functionality per note mode should be stored on the server. */ final var preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); final String prefKeyLastMode = getString(R.string.pref_key_last_note_mode); if (fragment instanceof NoteEditFragment) { preferences.edit().putString(prefKeyLastMode, getString(R.string.pref_value_mode_edit)).apply(); } else { preferences.edit().putString(prefKeyLastMode, getString(R.string.pref_value_mode_preview)).apply(); } fragment.onCloseNote(); if(isTaskRoot()) { Intent intent = new Intent(EditNoteActivity.this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); } else { finish(); } } @Override public void onNoteUpdated(Note note) { if (note != null) { binding.toolbar.setTitle(note.getTitle()); if (TextUtils.isEmpty(note.getCategory())) { binding.toolbar.setSubtitle(null); } else { binding.toolbar.setSubtitle(NoteUtil.extendCategory(note.getCategory())); } } } @Override public void onAccountPicked(@NonNull Account account) { fragment.moveNote(account); } @Override public void applyBrand(int mainColor, int textColor) { applyBrandToPrimaryToolbar(binding.appBar, binding.toolbar); } }