blob: 8441da0e504f24335057a525c0a5522a19d6ff6f [file] [log] [blame]
// Copyright 2014 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.components.devtools_bridge.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.app.Activity;
import android.app.ListFragment;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import org.chromium.components.devtools_bridge.RTCConfiguration;
import org.chromium.components.devtools_bridge.SessionBase;
import org.chromium.components.devtools_bridge.apiary.ApiaryClientFactory;
import org.chromium.components.devtools_bridge.apiary.TestApiaryClientFactory;
import org.chromium.components.devtools_bridge.commands.Command;
import org.chromium.components.devtools_bridge.commands.CommandSender;
import org.chromium.components.devtools_bridge.gcd.RemoteInstance;
import java.io.IOException;
import java.util.List;
/**
* Fragment of application for manual testing the DevTools bridge. Shows instances
* registered in GCD and lets unregister them.
*/
public abstract class RemoteInstanceListFragment extends ListFragment {
private static final String TAG = "RemoteInstanceListFragment";
private static final String NO_ID = "";
private static final int CODE_ACCOUNT_SELECTED = 1;
private final TestApiaryClientFactory mClientFactory = new TestApiaryClientFactory();
private ArrayAdapter<RemoteInstance> mListAdapter;
private String mOAuthToken;
private RemoteInstance mSelected;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListAdapter = new ArrayAdapter<RemoteInstance>(
getActivity(), android.R.layout.simple_list_item_1);
new UpdateListAction().execute();
setListAdapter(mListAdapter);
}
@Override
public void onDestroy() {
new AsyncTask<Void, Void, Void>() {
@Override
protected final Void doInBackground(Void... args) {
mClientFactory.close();
return null;
}
}.execute();
super.onDestroy();
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, container, savedInstanceState);
registerForContextMenu(root);
return root;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
mSelected = mListAdapter.getItem(position);
if (mOAuthToken == null) {
signIn();
} else {
getActivity().openContextMenu(view);
}
}
@Override
public void onCreateContextMenu(
ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, view, menuInfo);
if (mOAuthToken == null) return;
menu.add("Connect")
.setOnMenuItemClickListener(new ConnectAction())
.setEnabled(mSelected != null);
menu.add("Send invalid offer")
.setOnMenuItemClickListener(new SendInvalidOfferAction())
.setEnabled(mSelected != null);
menu.add("Delete")
.setOnMenuItemClickListener(new DeleteInstanceAction(mSelected))
.setEnabled(mSelected != null);
menu.add("Update list")
.setOnMenuItemClickListener(new UpdateListAction());
menu.add("Delete all")
.setOnMenuItemClickListener(new DeleteAllAction());
}
public void signIn() {
AccountManager manager = AccountManager.get(getActivity());
Intent intent = manager.newChooseAccountIntent(
null /* selectedAccount */,
null /* allowableAccounts */,
new String[] { "com.google" } /* allowableAccountTypes */,
true /* alwaysPromptForAccount */,
"Sign into GCD" /* descriptionOverrideText */,
null /* addAccountAuthTokenType */,
null /* addAccountRequiredFeatures */,
null /* addAccountOptions */);
startActivityForResult(intent, CODE_ACCOUNT_SELECTED);
}
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == CODE_ACCOUNT_SELECTED && resultCode == Activity.RESULT_OK) {
queryOAuthToken(new Account(
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME),
data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE)));
}
}
protected abstract void connect(String oAuthToken, String remoteInstanceId);
public void queryOAuthToken(Account accout) {
AccountManager manager = AccountManager.get(getActivity());
manager.getAuthToken(
accout,
"oauth2:" + ApiaryClientFactory.OAUTH_SCOPE, null /* options */, getActivity(),
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
mOAuthToken = future.getResult().getString(
AccountManager.KEY_AUTHTOKEN);
updateList();
} catch (Exception e) {
Log.d(TAG, "Failed to get token: ", e);
}
}
}, null);
}
public void updateList() {
new UpdateListAction().execute();
}
private final class ConnectAction implements MenuItem.OnMenuItemClickListener {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (mOAuthToken != null && mSelected != null) {
connect(mOAuthToken, mSelected.id);
return true;
}
return false;
}
}
private abstract class AsyncAction extends AsyncTask<Void, Void, Boolean>
implements MenuItem.OnMenuItemClickListener {
private final String mActionName;
protected AsyncAction(String actionName) {
mActionName = actionName;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
execute();
return true;
}
@Override
protected final Boolean doInBackground(Void... args) {
try {
doInBackgroundImpl();
return Boolean.TRUE;
} catch (IOException e) {
Log.e(TAG, mActionName + " failed", e);
return Boolean.FALSE;
}
}
@Override
protected void onPostExecute(Boolean success) {
if (Boolean.TRUE.equals(success)) {
onSuccess();
showToast(mActionName + " completed");
} else {
onFailure();
showToast(mActionName + " failed");
}
}
protected void showToast(String message) {
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
protected abstract void doInBackgroundImpl() throws IOException;
protected abstract void onSuccess();
protected void onFailure() {}
}
private final class SendInvalidOfferAction extends AsyncAction {
private final String mOAuthTokenCopy = mOAuthToken;
private final RemoteInstance mSelectedCopy = mSelected;
private IOException mException;
public SendInvalidOfferAction() {
super("Sending invalid offer");
}
@Override
protected final void doInBackgroundImpl() throws IOException {
if (mOAuthTokenCopy == null || mSelected == null) {
throw new IOException("Action cann't be applied.");
}
final String sessionId = "sessionId";
final String offer = "INVALID_OFFER";
final RTCConfiguration config = new RTCConfiguration();
CommandSender sender = new CommandSender() {
@Override
protected void send(Command command, Runnable completionCallback) {
try {
mClientFactory.newTestGCDClient(mOAuthTokenCopy)
.send(mSelectedCopy.id, command);
} catch (IOException e) {
mException = e;
command.setFailure("IO exception");
} finally {
completionCallback.run();
}
}
};
sender.startSession(sessionId, config, offer, new SessionBase.NegotiationCallback() {
@Override
public void onSuccess(String answer) {
mException = new IOException("Unexpected success");
}
@Override
public void onFailure(String errorMessage) {
Log.i(TAG, "Expected failure: " + errorMessage);
}
});
if (mException != null)
throw mException;
}
@Override
protected void onSuccess() {
}
}
private final class UpdateListAction extends AsyncAction {
private final String mOAuthTokenCopy = mOAuthToken;
private List<RemoteInstance> mResult;
public UpdateListAction() {
super("Updating instance list");
}
@Override
protected final void doInBackgroundImpl() throws IOException {
if (mOAuthTokenCopy == null) return;
mResult = mClientFactory.newTestGCDClient(mOAuthTokenCopy).fetchInstances();
}
@Override
protected void onSuccess() {
mListAdapter.clear();
if (mOAuthTokenCopy == null) {
mListAdapter.add(new RemoteInstance(NO_ID, "Sign in"));
} else if (mResult.size() > 0) {
mListAdapter.addAll(mResult);
} else {
mListAdapter.add(new RemoteInstance(NO_ID, "Empty list"));
}
}
@Override
protected void onFailure() {
mListAdapter.clear();
mListAdapter.add(new RemoteInstance(NO_ID, "Update failed"));
}
@Override
protected void showToast(String message) {}
}
private final class DeleteInstanceAction extends AsyncAction {
private final String mOAuthTokenCopy = mOAuthToken;
private final RemoteInstance mInstance;
public DeleteInstanceAction(RemoteInstance instance) {
super("Deleting instance");
mInstance = instance;
}
@Override
protected final void doInBackgroundImpl() throws IOException {
mClientFactory.newTestGCDClient(mOAuthTokenCopy).deleteInstance(mInstance.id);
}
@Override
protected void onSuccess() {
updateList();
}
}
private final class DeleteAllAction extends AsyncAction {
private final String mOAuthTokenCopy = mOAuthToken;
public DeleteAllAction() {
super("Deleting all");
}
@Override
protected final void doInBackgroundImpl() throws IOException {
for (RemoteInstance instance :
mClientFactory.newTestGCDClient(mOAuthTokenCopy).fetchInstances()) {
mClientFactory.newTestGCDClient(mOAuthTokenCopy).deleteInstance(instance.id);
}
}
@Override
protected void onSuccess() {
updateList();
}
}
}