blob: 9e534ce0bad14c25df1e977bfb8a71aa524e74f2 [file] [log] [blame]
// Copyright 2015 The Vanadium 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 io.v.android.apps.namespace_browser;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Parcel;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.google.common.collect.ImmutableList;
import java.util.List;
import io.v.android.libs.security.BlessingsManager;
import io.v.android.v23.V;
import io.v.android.v23.services.blessing.BlessingCreationException;
import io.v.android.v23.services.blessing.BlessingService;
import io.v.v23.context.VContext;
import io.v.v23.naming.GlobReply;
import io.v.v23.naming.MountEntry;
import io.v.v23.naming.MountedServer;
import io.v.v23.security.Blessings;
import io.v.v23.security.VPrincipal;
import io.v.v23.security.VSecurity;
import io.v.v23.verror.VException;
import io.v.v23.vom.VomUtil;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private static final String PREF_NAMESPACE_GLOB_ROOT = "pref_namespace_glob_root";
private static final String DEFAULT_NAMESPACE_GLOB_ROOT = "";
private static final String SAVED_VIEW_STATE_KEY = "browser_viewstate";
private static final int BLESSING_REQUEST = 1;
private VContext mBaseContext = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String root = PreferenceManager.getDefaultSharedPreferences(this).getString(
PREF_NAMESPACE_GLOB_ROOT, DEFAULT_NAMESPACE_GLOB_ROOT);
View dirView = findViewById(R.id.directory);
dirView.setPadding( // remove left padding for the root.
0, dirView.getPaddingTop(), dirView.getPaddingRight(), dirView.getPaddingBottom());
dirView.setTag(new GlobReply.Entry(
new MountEntry(root, ImmutableList.<MountedServer>of(), true, false)));
TextView nameView = (TextView) dirView.findViewById(R.id.name);
nameView.setText("/");
mBaseContext = V.init(this);
Drawable d = getResources().getDrawable(R.drawable.ic_account_box_black_36dp);
d.setColorFilter(new LightingColorFilter(Color.BLACK, Color.GRAY));
getBlessings();
}
@Override
protected void onStart() {
super.onStart();
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
Parcel parcel = Parcel.obtain();
ViewUtil.serializeView(findViewById(R.id.directory), parcel);
savedInstanceState.putByteArray(SAVED_VIEW_STATE_KEY, parcel.marshall());
parcel.recycle();
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
byte[] data = savedInstanceState.getByteArray(SAVED_VIEW_STATE_KEY);
Parcel parcel = Parcel.obtain();
parcel.unmarshall(data, 0, data.length);
parcel.setDataPosition(0);
LinearLayout dirView =
(LinearLayout) ViewUtil.deserializeView(parcel, getLayoutInflater());
parcel.recycle();
// Replace old directory view with the new one.
LinearLayout oldDirView = (LinearLayout) findViewById(R.id.directory);
ViewGroup parent = (ViewGroup) oldDirView.getParent();
int index = parent.indexOfChild(oldDirView);
parent.removeView(oldDirView);
parent.addView(dirView, index);
super.onRestoreInstanceState(savedInstanceState);
}
public void onItemClick(View view) {
switch (view.getId()) {
case R.id.directory:
handleDirectoryClick(view);
break;
case R.id.object:
handleObjectClick(view);
break;
case R.id.method:
handleMethodClick(view);
break;
default:
android.util.Log.e(
TAG, String.format("Click on an illegal view with id: %d", view.getId()));
}
}
private void handleDirectoryClick(View view) {
LinearLayout dirView = (LinearLayout) view;
if (!dirView.isActivated()) {
// Add new views.
new NameFetcher(dirView).execute();
// dirView will be updated only when the NameFetcher completes.
} else {
// Remove all but the first view.
if (dirView.getChildCount() > 1) {
dirView.removeViews(1, dirView.getChildCount() - 1);
}
ViewUtil.updateDirectoryView(dirView, false);
}
}
private void handleObjectClick(View view) {
LinearLayout objView = (LinearLayout) view;
if (!objView.isActivated()) {
// Add new views.
new MethodFetcher(objView).execute();
// objView will be updated only when the NameFetcher completes.
} else {
// Remove all but the first view.
if (objView.getChildCount() > 1) {
objView.removeViews(1, objView.getChildCount() - 1);
}
ViewUtil.updateObjectView(objView, false);
}
}
private void handleMethodClick(View view) {
LinearLayout methodView = (LinearLayout) view;
// Add claiming logic here.
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_account: {
refreshBlessings();
return true;
}
case R.id.action_settings: {
Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(intent);
return true;
}
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case BLESSING_REQUEST:
try {
byte[] blessingVom = BlessingService.extractBlessingReply(resultCode, data);
Blessings blessings = (Blessings) VomUtil.decode(blessingVom, Blessings.class);
BlessingsManager.addBlessings(this, blessings);
Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show();
getBlessings();
} catch (BlessingCreationException e) {
String msg = "Couldn't retrieve blessing from blessing service: " + e.getMessage();
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
android.util.Log.e(TAG, msg);
} catch (VException e) {
String msg = "Couldn't decode and store blessing: " + e.getMessage();
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
android.util.Log.e(TAG, msg);
}
return;
}
}
private void refreshBlessings() {
Intent intent = BlessingService.newBlessingIntent(this);
startActivityForResult(intent, BLESSING_REQUEST);
}
private void getBlessings() {
Blessings blessings = null;
try {
// See if there are blessings stored in shared preferences.
blessings = BlessingsManager.getBlessings(this);
} catch (VException e) {
String msg = "Error getting blessings from shared preferences " + e.getMessage();
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
android.util.Log.e(TAG, msg);
}
if (blessings == null) {
// Request new blessings from the account manager. If successful, this will eventually
// trigger another call to this method, with BlessingsManager.getBlessings() returning
// non-null blessings.
refreshBlessings();
return;
}
try {
// Update local state with the new blessings.
VPrincipal p = V.getPrincipal(mBaseContext);
p.blessingStore().setDefaultBlessings(blessings);
VSecurity.addToRoots(p, blessings);
} catch (VException e) {
String msg = String.format(
"Couldn't set local blessing %s: %s", blessings, e.getMessage());
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
android.util.Log.e(TAG, msg);
}
}
private class NameFetcher extends AsyncTask<Void, Void, List<GlobReply>> {
final ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
final LinearLayout dirView;
String errorMsg = "";
NameFetcher(LinearLayout dirView) {
this.dirView = dirView;
}
@Override
protected void onPreExecute() {
progressDialog.setMessage("Fetching Names...");
progressDialog.show();
}
@Override
protected List<GlobReply> doInBackground(Void... args) {
GlobReply entry = (GlobReply) dirView.getTag();
if (!(entry instanceof GlobReply.Entry)) {
return ImmutableList.<GlobReply>of();
}
try {
return Namespace.glob(((GlobReply.Entry) entry).getElem().getName(), mBaseContext);
} catch (VException e) {
errorMsg = "Error fetching names: " + e.getMessage();
return null;
}
}
@Override
protected void onPostExecute(List<GlobReply> replies) {
progressDialog.dismiss();
ViewUtil.updateDirectoryView(dirView, true);
if (replies == null) {
Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
android.util.Log.e(TAG, errorMsg);
return;
}
GlobReply.Entry parentEntry = (GlobReply.Entry) dirView.getTag();
for (GlobReply reply : replies) {
if (reply instanceof GlobReply.Entry) {
MountEntry entry = ((GlobReply.Entry) reply).getElem();
String text = "";
if (parentEntry.getName().isEmpty()) {
text = entry.getName();
} else {
if (!entry.getName().startsWith(parentEntry.getElem().getName() + "/")) {
android.util.Log.e(TAG, String.format(
"Entry %s doesn't start with parent prefix %s",
entry.getName(), parentEntry.getElem().getName() + "/"));
// TODO(sjr): figure out the correct name format
// continue;
}
text = entry.getName();
}
LinearLayout childView =
(entry.getServers() == null || entry.getServers().size() <= 0)
? ViewUtil.createDirectoryView(text, reply, getLayoutInflater())
// sub-dir
: ViewUtil.createObjectView(text, reply,
getLayoutInflater()); // object
dirView.addView(childView);
} else if (reply instanceof GlobReply.Error) {
String msg = String.format("Couldn't fetch namespace subtree \"%s\": %s",
((GlobReply.Error) reply).getElem().getName(),
((GlobReply.Error) reply).getElem().getError().getMessage());
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show();
android.util.Log.e(TAG, msg);
}
}
}
}
private class MethodFetcher extends AsyncTask<Void, Void, List<String>> {
final ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
final LinearLayout objView;
String errorMsg = "";
MethodFetcher(LinearLayout objView) {
this.objView = objView;
}
@Override
protected void onPreExecute() {
progressDialog.setMessage("Fetching Methods...");
progressDialog.show();
}
@Override
protected List<String> doInBackground(Void... args) {
GlobReply entry = (GlobReply) objView.getTag();
if (!(entry instanceof GlobReply.Entry)) {
return ImmutableList.<String>of();
}
try {
return Methods.get(((GlobReply.Entry) entry).getElem().getName(), mBaseContext);
} catch (VException e) {
errorMsg = "Error fetching methods: " + e.getMessage();
return null;
}
}
@Override
protected void onPostExecute(List<String> methods) {
progressDialog.dismiss();
ViewUtil.updateObjectView(objView, true);
if (methods == null) {
Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
android.util.Log.e(TAG, errorMsg);
return;
}
GlobReply reply = (GlobReply) objView.getTag();
for (String method : methods) {
LinearLayout childView =
ViewUtil.createMethodView(method, reply, getLayoutInflater());
objView.addView(childView);
}
}
}
}