blob: eea8107f40d68ad8cae5887d5e5f08a37b170506 [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.account_manager;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.CheckedTextView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.v.android.v23.V;
import io.v.v23.context.VContext;
import io.v.v23.security.BlessingPattern;
import io.v.v23.security.BlessingStore;
import io.v.v23.security.Blessings;
import io.v.v23.security.VCertificate;
import io.v.v23.security.VPrincipal;
import io.v.v23.security.VSecurity;
import io.v.v23.security.WireBlessings;
import io.v.v23.vom.VomUtil;
/**
* Prompts user to choose among his/her Vanadium Blessings.
*/
public class BlessingChooserActivity extends Activity {
public static final String TAG = "BlessingChooserActivity";
private static final int REQUEST_CODE_CREATE_ACCOUNT = 1000;
public static final String CANCELED_REQUEST = "User Canceled Blessing Selection";
public static final String EXTRA_BLESSING_SET = "EXTRA_BLESSING_SET";
public static final int ALL_BLESSINGS = 1;
public static final int SIGNING_BLESSINGS = 2;
BlessingStore mBlessingStore = null;
HashMap<Integer, Blessings> mBlessings = null;
int mBlessingSet = ALL_BLESSINGS;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
VContext context = V.init(this);
VPrincipal principal = V.getPrincipal(context);
mBlessingStore = principal.blessingStore();
mBlessings = new HashMap<>();
Intent intent = getIntent();
if (intent == null) {
replyWithError("No intent found");
return;
}
mBlessingSet = intent.getIntExtra(EXTRA_BLESSING_SET, ALL_BLESSINGS);
setContentView(R.layout.activity_blessing_chooser);
ScrollView scrollView = (ScrollView) findViewById(R.id.scroll_blessings);
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnViewGlobalLayoutListener(scrollView));
if (hasBlessings()) {
display();
} else {
// No Vanadium blessings available: prompt the user to fetch an blessing from his/her
// identity service.
createBlessings();
}
}
private class OnViewGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private static final int MAX_HEIGHT = 600;
View mmView = null;
public OnViewGlobalLayoutListener(ScrollView v) {
mmView = v;
}
@Override
public void onGlobalLayout() {
if (mmView.getHeight() > MAX_HEIGHT) {
mmView.getLayoutParams().height = MAX_HEIGHT;
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_CREATE_ACCOUNT:
if (resultCode != RESULT_OK && !hasBlessings()) {
// No blessing to display. To avoid putting the user into a potentially
// infinite blessing creation loop, we reply with an error here.
replyWithError("Couldn't create a Vanadium blessing.");
return;
}
display();
}
}
private void display() {
if (hasBlessings()) {
LinearLayout blessingsView = (LinearLayout) findViewById(R.id.chooser_blessings);
blessingsView.removeAllViews();
Map<BlessingPattern, Blessings> peerMap = mBlessingStore.peerBlessings();
for (BlessingPattern pattern : peerMap.keySet()) {
List<List<VCertificate>> chains = null;
switch (mBlessingSet) {
case ALL_BLESSINGS:
chains = peerMap.get(pattern).getCertificateChains();
break;
case SIGNING_BLESSINGS:
chains = peerMap.get(pattern).signingBlessings().getCertificateChains();
break;
default:
chains = peerMap.get(pattern).getCertificateChains();
break;
}
for (List<VCertificate> certChain : chains) {
Blessings blessing = Blessings.create(new WireBlessings(ImmutableList.of(certChain)));
mBlessings.put(blessingsView.getChildCount(), blessing);
LinearLayout blessingView =
(LinearLayout)
getLayoutInflater().inflate(R.layout.chooser_blessing, null);
CheckedTextView view =
(CheckedTextView) blessingView.findViewById(R.id.chooser_blessing);
view.setText(name(certChain));
blessingsView.addView(blessingView);
}
}
} else {
replyWithError("Couldn't find newly obtained blessings?!");
}
}
// Starts the blessing creation activity.
private void createBlessings() {
startActivityForResult(new Intent(this, AccountActivity.class), REQUEST_CODE_CREATE_ACCOUNT);
}
private boolean hasBlessings() {
return !mBlessingStore.peerBlessings().values().isEmpty();
}
public void onBlessingSelected(View v) {
CheckedTextView blessingView = (CheckedTextView) v.findViewById(R.id.chooser_blessing);
blessingView.setChecked(!blessingView.isChecked());
}
public void onOK(View v) {
LinearLayout blessingsView = (LinearLayout) findViewById(R.id.chooser_blessings);
ArrayList<Blessings> selectedBlessings = new ArrayList<>();
for (int i = 0; i < blessingsView.getChildCount(); ++i) {
CheckedTextView blessingView =
(CheckedTextView)
blessingsView.getChildAt(i).findViewById(R.id.chooser_blessing);
if (blessingView.isChecked()) {
selectedBlessings.add(mBlessings.get(i));
}
}
if (selectedBlessings.isEmpty()) {
Toast.makeText(this, "Must select a blessing.", Toast.LENGTH_LONG).show();
return;
}
replyWithSuccess(selectedBlessings.toArray(new Blessings[selectedBlessings.size()]));
}
public void onCancel(View v) {
replyWithError(CANCELED_REQUEST);
}
public void onAdd(View v) {
createBlessings();
}
private void replyWithSuccess(Blessings[] blessings) {
Intent intent = new Intent();
try {
Blessings union = VSecurity.unionOfBlessings(blessings);
intent.putExtra(Constants.REPLY, VomUtil.encode(union, Blessings.class));
setResult(RESULT_OK, intent);
finish();
} catch (Exception e) {
replyWithError("Could not serialize blessings: " + e.getMessage());
}
}
private void replyWithError(String error) {
android.util.Log.e(TAG, "Error choosing blessings: " + error);
Intent intent = new Intent();
intent.putExtra(Constants.ERROR, error);
setResult(RESULT_CANCELED, intent);
finish();
}
private static String name(List<VCertificate> certChain) {
String name = "";
for (VCertificate certificate : certChain) {
name += certificate.getExtension() +
io.v.v23.security.Constants.CHAIN_SEPARATOR;
}
return name;
}
}