| // 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.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 com.google.common.util.concurrent.FutureCallback; |
| import com.google.common.util.concurrent.Futures; |
| import com.google.common.util.concurrent.ListenableFuture; |
| |
| import org.joda.time.Duration; |
| |
| import java.util.List; |
| |
| import io.v.android.libs.security.BlessingsManager; |
| import io.v.android.v23.V; |
| import io.v.v23.InputChannelCallback; |
| import io.v.v23.InputChannels; |
| import io.v.v23.context.VContext; |
| import io.v.v23.naming.GlobError; |
| import io.v.v23.naming.GlobReply; |
| import io.v.v23.naming.MountEntry; |
| import io.v.v23.naming.MountedServer; |
| import io.v.v23.security.Blessings; |
| |
| public class MainActivity extends Activity { |
| private static final String TAG = "MainActivity"; |
| private static final String BLESSINGS_KEY = "BlessingsKey"; |
| |
| 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 VContext mBaseContext; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_main); |
| mBaseContext = V.init(this); |
| 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("/"); |
| Drawable d = getResources().getDrawable(R.drawable.ic_account_box_black_36dp); |
| d.setColorFilter(new LightingColorFilter(Color.BLACK, Color.GRAY)); |
| Futures.addCallback(BlessingsManager.getBlessings(mBaseContext, this, BLESSINGS_KEY, true), |
| new FutureCallback<Blessings>() { |
| @Override |
| public void onSuccess(Blessings result) { |
| android.util.Log.i(TAG, "Success."); |
| } |
| @Override |
| public void onFailure(Throwable t) { |
| android.util.Log.e(TAG, "Couldn't get blessings: " + t.getMessage()); |
| } |
| }); |
| } |
| |
| @Override |
| protected void onDestroy() { |
| super.onDestroy(); |
| mBaseContext.cancel(); |
| } |
| |
| @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. |
| fetchNames(dirView); |
| } 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. |
| fetchMethods(objView); |
| } 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: { |
| Futures.addCallback( |
| BlessingsManager.mintBlessings(mBaseContext, this, BLESSINGS_KEY, true), |
| new FutureCallback<Blessings>() { |
| @Override |
| public void onSuccess(Blessings result) { |
| Toast.makeText( |
| MainActivity.this, "Success.", Toast.LENGTH_SHORT).show(); |
| } |
| |
| @Override |
| public void onFailure(Throwable t) { |
| Log.e(TAG, "Couldn't get blessings: " + t.getMessage()); |
| } |
| }); |
| return true; |
| } |
| case R.id.action_settings: { |
| Intent intent = new Intent(MainActivity.this, SettingsActivity.class); |
| startActivity(intent); |
| return true; |
| } |
| default: |
| return super.onOptionsItemSelected(item); |
| } |
| } |
| |
| private void fetchNames(final LinearLayout dirView) { |
| ViewUtil.updateDirectoryView(dirView, true); |
| GlobReply entry = (GlobReply) dirView.getTag(); |
| if (!(entry instanceof GlobReply.Entry)) { |
| return; |
| } |
| final MountEntry dirEntry = ((GlobReply.Entry) entry).getElem(); |
| final ProgressDialog progressDialog = new ProgressDialog(MainActivity.this); |
| progressDialog.setMessage("Fetching Names..."); |
| progressDialog.show(); |
| String root = dirEntry.getName(); |
| io.v.v23.namespace.Namespace n = V.getNamespace(mBaseContext); |
| VContext ctxT = mBaseContext.withTimeout(new Duration(20000)); // 20s |
| Futures.addCallback( |
| InputChannels.withCallback(n.glob(ctxT, root.isEmpty() ? "*" : root + "/*"), |
| new InputChannelCallback<GlobReply>() { |
| @Override |
| public ListenableFuture<Void> onNext(GlobReply reply) { |
| if (reply instanceof GlobReply.Error) { |
| GlobError error = ((GlobReply.Error) reply).getElem(); |
| String msg = String.format( |
| "Couldn't fetch namespace subtree \"%s\": %s", |
| error.getName(), |
| error.getError().getMessage()); |
| android.util.Log.e(TAG, msg); |
| return null; |
| } |
| MountEntry entry = ((GlobReply.Entry) reply).getElem(); |
| String text = ""; |
| if (dirEntry.getName().isEmpty()) { |
| text = entry.getName(); |
| } else if (entry.getName().startsWith(dirEntry.getName() + "/")) { |
| text = entry.getName().substring(dirEntry.getName().length() + 1); |
| } else { |
| Log.e(TAG, String.format( |
| "Entry %s doesn't start with parent prefix %s", |
| entry.getName(), dirEntry.getName() + "/")); |
| return null; |
| } |
| LinearLayout childView = |
| (entry.getServers() == null || entry.getServers().size() <= 0) |
| ? ViewUtil.createDirectoryView( |
| text, reply, getLayoutInflater()) |
| : ViewUtil.createObjectView( |
| text, reply, getLayoutInflater()); |
| dirView.addView(childView); |
| return null; |
| } |
| }), new FutureCallback<Void>() { |
| @Override |
| public void onSuccess(Void result) { |
| progressDialog.dismiss(); |
| } |
| @Override |
| public void onFailure(Throwable t) { |
| progressDialog.dismiss(); |
| Log.e(TAG, "Error fetching names: " + t.getMessage()); |
| } |
| }); |
| } |
| |
| private void fetchMethods(final LinearLayout objView) { |
| ViewUtil.updateObjectView(objView, true); |
| final GlobReply entry = (GlobReply) objView.getTag(); |
| if (!(entry instanceof GlobReply.Entry)) { |
| return; |
| } |
| final MountEntry objEntry = ((GlobReply.Entry) entry).getElem(); |
| final ProgressDialog progressDialog = new ProgressDialog(MainActivity.this); |
| progressDialog.setMessage("Fetching Methods..."); |
| progressDialog.show(); |
| Futures.addCallback(Methods.get(mBaseContext, objEntry.getName()), |
| new FutureCallback<List<String>>() { |
| @Override |
| public void onSuccess(List<String> methods) { |
| for (String method : methods) { |
| LinearLayout childView = |
| ViewUtil.createMethodView(method, entry, getLayoutInflater()); |
| objView.addView(childView); |
| } |
| progressDialog.dismiss(); |
| } |
| @Override |
| public void onFailure(Throwable t) { |
| progressDialog.dismiss(); |
| Log.e(TAG, "Error fetching methods: " + t.getMessage()); |
| } |
| }); |
| } |
| } |