| // 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); |
| } |
| } |
| } |
| } |