blob: f04aba107ee1c3cb06ed2418bb65c32da884981a [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.bookmark;
import android.Manifest;
import android.app.Fragment;
import android.app.FragmentManager.OnBackStackChangedListener;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Process;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.WindowManager;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.SuppressFBWarnings;
import org.chromium.base.library_loader.ProcessInitException;
import org.chromium.chrome.browser.ChromeApplication;
import org.chromium.chrome.browser.ChromeBrowserProviderClient;
import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.DeviceFormFactor;
/**
* This class provides the adding bookmark UI. It is also required by
* android.provider.Browser.saveBookmark
*/
public class ManageBookmarkActivity extends FragmentActivity {
private static final String TAG = "ManageBookmarkActivity";
/* TODO(gb-deprecation): Use android.provider.BrowserContract.Bookmarks.IS_FOLDER */
public static final String BOOKMARK_INTENT_IS_FOLDER = "folder";
/* TODO(gb-deprecation): Use android.provider.BrowserContract.Bookmarks.TITLE */
public static final String BOOKMARK_INTENT_TITLE = "title";
/* TODO(gb-deprecation): Use android.provider.BrowserContract.Bookmarks.URL */
public static final String BOOKMARK_INTENT_URL = "url";
/* TODO(gb-deprecation): Use android.provider.BrowserContract.Bookmarks._ID */
public static final String BOOKMARK_INTENT_ID = "_id";
/**
* The tag used when adding the base add/edit bookmark fragment.
*/
@VisibleForTesting
public static final String BASE_ADD_EDIT_FRAGMENT_TAG = "AddEdit";
/**
* The tag used when adding the folder selection fragment triggered by the base
* add/edit bookmark fragment.
*/
@VisibleForTesting
public static final String BASE_SELECT_FOLDER_FRAGMENT_TAG = "SelectFolder";
/**
* The tag used when adding the add new bookmark folder fragment triggered by the initial
* folder selection fragment.
*/
@VisibleForTesting
public static final String ADD_FOLDER_FRAGMENT_TAG = "AddFolder";
/**
* The tag used when adding the folder selection fragment triggered by the add new bookmark
* folder fragment.
*/
protected static final String ADD_FOLDER_SELECT_FOLDER_FRAGMENT_TAG = "AddFolderSelectFolder";
/**
* Internally used to temporarily disable the back button while performing non-cancelable
* asynchronous tasks that modify the bookmark model.
*/
private boolean mIsBackEnabled = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
if (getApplication() instanceof ChromeApplication) {
((ChromeApplication) getApplication())
.startBrowserProcessesAndLoadLibrariesSync(true);
}
} catch (ProcessInitException e) {
Log.e(TAG, "Unable to load native library.", e);
ChromeApplication.reportStartupErrorAndExit(e);
return;
}
if (!DeviceFormFactor.isTablet(this)) {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
if (savedInstanceState == null) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(
android.R.id.content, generateBaseFragment(), BASE_ADD_EDIT_FRAGMENT_TAG);
fragmentTransaction.commit();
} else {
initializeFragmentState();
}
// When adding or removing fragments, ensure the keyboard is hidden if visible as the
// editing fields are no longer on the screen.
getFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
UiUtils.hideKeyboard(findViewById(android.R.id.content));
}
});
if (checkPermission(Manifest.permission.NFC, Process.myPid(), Process.myUid())
== PackageManager.PERMISSION_GRANTED) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter != null) nfcAdapter.setNdefPushMessage(null, this);
}
}
@Override
public void finish() {
super.finish();
// Disables closing animation only for large/xlarge screen because the activity shows in
// fullscreen in other cases and so closing animation doesn't look unnatural.
if (getResources().getConfiguration().isLayoutSizeAtLeast(
Configuration.SCREENLAYOUT_SIZE_LARGE)) {
overridePendingTransition(0, 0);
}
}
@Override
public void onBackPressed() {
if (!mIsBackEnabled) return;
if (getFragmentManager().getBackStackEntryCount() == 0) {
finish();
} else {
getFragmentManager().popBackStackImmediate();
}
}
/**
* Initializes the fragment state after a state restoration.
* <p>
* Reinitializes all of the event listeners on the fragments as they are not persisted across
* activity recreation (and were referencing the old activity anyway).
* <p>
* The hidden state of the fragments are also not persisted across activity changes, so we need
* to hide and show the fragments accordingly (since we know that hierarchy of the fragments
* we can do this as a simple nested conditional).
*/
private void initializeFragmentState() {
AddEditBookmarkFragment baseAddEditFragment = (AddEditBookmarkFragment) getFragmentManager()
.findFragmentByTag(BASE_ADD_EDIT_FRAGMENT_TAG);
setActionListenerOnAddEdit(baseAddEditFragment);
Fragment selectFolderFragment =
getFragmentManager().findFragmentByTag(BASE_SELECT_FOLDER_FRAGMENT_TAG);
if (selectFolderFragment != null) {
setActionListenerOnFolderSelection(
(SelectBookmarkFolderFragment) selectFolderFragment);
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(baseAddEditFragment);
Fragment addFolderFragment =
getFragmentManager().findFragmentByTag(ADD_FOLDER_FRAGMENT_TAG);
if (addFolderFragment != null) {
fragmentTransaction.hide(selectFolderFragment);
setActionListenerOnAddEdit((AddEditBookmarkFragment) addFolderFragment);
Fragment addFolderSelectFolderFragment = getFragmentManager().findFragmentByTag(
ADD_FOLDER_SELECT_FOLDER_FRAGMENT_TAG);
if (addFolderSelectFolderFragment != null) {
setActionListenerOnFolderSelection(
(SelectBookmarkFolderFragment) addFolderSelectFolderFragment);
fragmentTransaction.hide(addFolderFragment);
fragmentTransaction.show(addFolderSelectFolderFragment);
} else {
fragmentTransaction.show(addFolderFragment);
}
} else {
fragmentTransaction.show(selectFolderFragment);
}
fragmentTransaction.commit();
}
}
/**
* Creates the base add/edit bookmark fragment based on the intent passed to this activity.
*
* @return The appropriate fragment based on the intent parameters.
*/
@SuppressFBWarnings("NP_NULL_ON_SOME_PATH")
private AddEditBookmarkFragment generateBaseFragment() {
if (getIntent() == null) {
throw new IllegalArgumentException("intent can not be null");
}
Intent intent = getIntent();
boolean isFolder = IntentUtils.safeGetBooleanExtra(
intent, BOOKMARK_INTENT_IS_FOLDER, false);
String name = IntentUtils.safeGetStringExtra(intent, BOOKMARK_INTENT_TITLE);
String url = IntentUtils.safeGetStringExtra(intent, BOOKMARK_INTENT_URL);
if (url != null) url = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url);
long bookmarkId = IntentUtils.safeGetLongExtra(
intent, BOOKMARK_INTENT_ID, ChromeBrowserProviderClient.INVALID_BOOKMARK_ID);
AddEditBookmarkFragment addEditFragment = AddEditBookmarkFragment.newInstance(
isFolder, bookmarkId, name, url);
setActionListenerOnAddEdit(addEditFragment);
return addEditFragment;
}
/**
* Creates and sets an action listener on the add/edit bookmark fragment.
* @param addEditFragment The fragment that the listener should be attached to.
*/
private void setActionListenerOnAddEdit(final AddEditBookmarkFragment addEditFragment) {
addEditFragment.setOnActionListener(new AddEditBookmarkFragment.OnActionListener() {
@Override
public void onCancel() {
finishAddEdit();
}
@Override
public void onNodeEdited(long id) {
finishAddEdit();
}
@Override
public void onFolderCreated(long id, String name) {
finishAddEdit();
if (getFragmentManager().getBackStackEntryCount() != 0) {
((SelectBookmarkFolderFragment) addEditFragment.getTargetFragment())
.executeFolderSelection(id, name);
}
}
@Override
public void triggerFolderSelection() {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
boolean isRoot = getFragmentManager().getBackStackEntryCount() == 0;
SelectBookmarkFolderFragment selectFolder =
SelectBookmarkFolderFragment.newInstance(
// Only allow the root add/edit bookmark UI to create new bookmark
// folders during selection.
isRoot,
addEditFragment.getParentFolderId(),
addEditFragment.isFolder());
selectFolder.setTargetFragment(addEditFragment, 0);
setActionListenerOnFolderSelection(selectFolder);
String tag = isRoot ? BASE_SELECT_FOLDER_FRAGMENT_TAG
: ADD_FOLDER_SELECT_FOLDER_FRAGMENT_TAG;
fragmentTransaction.add(android.R.id.content, selectFolder, tag);
fragmentTransaction.hide(addEditFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
@Override
public void onRemove() {
finishAddEdit();
}
@Override
public void setBackEnabled(boolean enabled) {
mIsBackEnabled = enabled;
}
private void finishAddEdit() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
finish();
} else {
getFragmentManager().popBackStackImmediate();
}
}
});
}
/**
* Creates and sets an action listener on the bookmark folder selection fragment.
* @param selectFolder The fragment that the listener should be attached to.
*/
private void setActionListenerOnFolderSelection(
final SelectBookmarkFolderFragment selectFolder) {
selectFolder.setOnActionListener(new SelectBookmarkFolderFragment.OnActionListener() {
@Override
public void triggerNewFolderCreation(long selectedFolderId, String selectedFolderName) {
newFolder(selectFolder, selectedFolderId, selectedFolderName);
}
});
}
/**
* Create and display the fragment for creating a new bookmark folder.
* @param triggeringFragment The fragment that requested the new folder fragment to be shown.
* @param parentFolderId The ID of the bookmark folder to used as the default parent of the
* folder.
* @param parentName The name of the bookmark folder to be used at the default parent.
*/
private void newFolder(Fragment triggeringFragment, long parentFolderId, String parentName) {
AddEditBookmarkFragment newFolderFragment =
AddEditBookmarkFragment.newAddNewFolderInstance(parentFolderId, parentName);
newFolderFragment.setTargetFragment(triggeringFragment, 0);
setActionListenerOnAddEdit(newFolderFragment);
getFragmentManager().beginTransaction()
.hide(triggeringFragment)
.add(android.R.id.content, newFolderFragment, ADD_FOLDER_FRAGMENT_TAG)
.addToBackStack(null)
.commit();
}
}