blob: 4e46a2b6bcc02a5e4eafbb02252f5b26c0add29f [file] [log] [blame]
// Copyright 2019 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.weblayer.shell;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.text.InputType;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import org.chromium.weblayer.BrowserController;
import org.chromium.weblayer.BrowserFragment;
import org.chromium.weblayer.BrowserFragmentController;
import org.chromium.weblayer.BrowserObserver;
import org.chromium.weblayer.Profile;
import org.chromium.weblayer.UnsupportedVersionException;
import org.chromium.weblayer.WebLayer;
import java.util.List;
/**
* Activity for managing the Demo Shell.
*/
public class WebLayerShellActivity extends FragmentActivity {
private static final String TAG = "WebLayerShell";
private static final String KEY_MAIN_VIEW_ID = "mainViewId";
private Profile mProfile;
private BrowserFragmentController mBrowserFragmentController;
private BrowserController mBrowserController;
private EditText mUrlView;
private ProgressBar mLoadProgressBar;
private View mMainView;
private int mMainViewId;
private ViewGroup mTopContentsContainer;
private BrowserFragment mFragment;
public BrowserController getBrowserController() {
return mBrowserController;
}
public BrowserFragmentController getBrowserFragmentController() {
return mBrowserFragmentController;
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout mainView = new LinearLayout(this);
if (savedInstanceState == null) {
mMainViewId = View.generateViewId();
} else {
mMainViewId = savedInstanceState.getInt(KEY_MAIN_VIEW_ID);
}
mainView.setId(mMainViewId);
mMainView = mainView;
setContentView(mainView);
mUrlView = new EditText(this);
mUrlView.setId(View.generateViewId());
mUrlView.setSelectAllOnFocus(true);
mUrlView.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
mUrlView.setImeOptions(EditorInfo.IME_ACTION_GO);
// The background of the top-view must be opaque, otherwise it bleeds through to the
// cc::Layer that mirrors the contents of the top-view.
mUrlView.setBackgroundColor(0xFFa9a9a9);
mUrlView.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((actionId != EditorInfo.IME_ACTION_GO)
&& (event == null || event.getKeyCode() != KeyEvent.KEYCODE_ENTER
|| event.getAction() != KeyEvent.ACTION_DOWN)) {
return false;
}
loadUrl(mUrlView.getText().toString());
return true;
}
});
mLoadProgressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
mLoadProgressBar.setIndeterminate(false);
mLoadProgressBar.setMax(100);
mLoadProgressBar.setVisibility(View.INVISIBLE);
// The progress bar sits above the URL bar in Z order and at its bottom in Y.
mTopContentsContainer = new RelativeLayout(this);
mTopContentsContainer.addView(mUrlView,
new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
RelativeLayout.LayoutParams progressLayoutParams = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
progressLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mUrlView.getId());
progressLayoutParams.setMargins(0, 0, 0, -10);
mTopContentsContainer.addView(mLoadProgressBar, progressLayoutParams);
try {
// This ensures asynchronous initialization of WebLayer on first start of activity.
// If activity is re-created during process restart, FragmentManager attaches
// BrowserFragment immediately, resulting in synchronous init. By the time this line
// executes, the synchronous init has already happened.
WebLayer.create(getApplication()).addCallback(webLayer -> onWebLayerReady(
savedInstanceState));
} catch (UnsupportedVersionException e) {
throw new RuntimeException("Failed to initialize WebLayer", e);
}
}
private void onWebLayerReady(Bundle savedInstanceState) {
if (isFinishing() || isDestroyed()) return;
mFragment = getOrCreateBrowserFragment(savedInstanceState);
mBrowserFragmentController = mFragment.getController();
mProfile = mBrowserFragmentController.getProfile();
mBrowserFragmentController.setTopView(mTopContentsContainer);
mBrowserController = mBrowserFragmentController.getBrowserController();
String startupUrl = getUrlFromIntent(getIntent());
if (TextUtils.isEmpty(startupUrl)) {
startupUrl = "http://google.com";
}
loadUrl(startupUrl);
mBrowserController.addObserver(new BrowserObserver() {
@Override
public void visibleUrlChanged(Uri uri) {
mUrlView.setText(uri.toString());
}
@Override
public void loadingStateChanged(boolean isLoading, boolean toDifferentDocument) {
mLoadProgressBar.setVisibility(
isLoading && toDifferentDocument ? View.VISIBLE : View.INVISIBLE);
}
@Override
public void loadProgressChanged(double progress) {
mLoadProgressBar.setProgress((int) Math.round(100 * progress));
}
});
}
private BrowserFragment getOrCreateBrowserFragment(Bundle savedInstanceState) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (savedInstanceState != null) {
// FragmentManager could have re-created the fragment.
List<Fragment> fragments = fragmentManager.getFragments();
if (fragments.size() > 1) {
throw new IllegalStateException("More than one fragment added, shouldn't happen");
}
if (fragments.size() == 1) {
return (BrowserFragment) fragments.get(0);
}
}
BrowserFragment fragment = WebLayer.createBrowserFragment(null);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(mMainViewId, fragment);
// Note the commitNow() instead of commit(). We want the fragment to get attached to
// activity synchronously, so we can use all the functionality immediately. Otherwise we'd
// have to wait until the commit is executed.
transaction.commitNow();
return fragment;
}
@Override
protected void onStart() {
super.onStart();
}
public void loadUrl(String url) {
mBrowserController.getNavigationController().navigate(Uri.parse(sanitizeUrl(url)));
mUrlView.clearFocus();
}
private static String getUrlFromIntent(Intent intent) {
return intent != null ? intent.getDataString() : null;
}
/**
* Given an URL, this performs minimal sanitizing to ensure it will be valid.
* @param url The url to be sanitized.
* @return The sanitized URL.
*/
public static String sanitizeUrl(String url) {
if (url == null) return null;
if (url.startsWith("www.") || url.indexOf(":") == -1) url = "http://" + url;
return url;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// When restoring Fragments, FragmentManager tries to put them in the containers with same
// ids as before.
outState.putInt(KEY_MAIN_VIEW_ID, mMainViewId);
}
}