blob: 03a3ad4cc7bfcaf4789ece0c4eb118817eb295c5 [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.baku.toolkit;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import io.v.android.v23.V;
import io.v.baku.toolkit.blessings.BlessingsManagerBlessingsProvider;
import io.v.baku.toolkit.blessings.BlessingsProvider;
import io.v.baku.toolkit.blessings.BlessingsUtils;
import io.v.baku.toolkit.debug.DebugFragment;
import io.v.baku.toolkit.debug.DebugUtils;
import io.v.v23.Options;
import io.v.v23.context.VContext;
import io.v.v23.security.Blessings;
import io.v.v23.verror.VException;
import java8.util.function.BiFunction;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
/**
* Android context mix-in incorporating common Vanadium utilities. These include:
* <ul>
* <li>Vanadium initialization during {@code onCreate}; context available via
* {@code getVContext}</li>
* <li>Blessings management, available via {@link BlessingsProvider#getRxBlessings()
* getBlessingsProvider().getRxBlessings()}. Upon {@code subscribe}, blessings are refreshed from
* the {@code BlessingsManager} or sought from the {@code BlessingsProvider} (by default, the
* Vanadium {@link io.v.android.libs.security.BlessingsManager}).</li>
* </ul>
* Default activity extensions incorporating this mix-in are available:
* <ul>
* <li>{@link VActivity} (extends {@link Activity})</li>
* <li>{@link VAppCompatActivity} (extends {@link android.support.v7.app.AppCompatActivity})</li>
* </ul>
* Since Java doesn't actually support multiple inheritance, clients requiring custom inheritance
* hierarchies will need to wire in manually, like any of the examples above.
*/
@Accessors(prefix = "m")
@Slf4j
public class VAndroidContextMixin<T extends Context> implements VAndroidContextTrait<T> {
public static SharedPreferences getVanadiumPreferences(final Context androidContext) {
return androidContext.getSharedPreferences(VANADIUM_OPTIONS_SHARED_PREFS,
Context.MODE_PRIVATE);
}
@Getter
private final T mAndroidContext;
@Getter
private final BlessingsProvider mBlessingsProvider;
@Getter
private final ErrorReporter mErrorReporter;
@Getter
private final SharedPreferences mVanadiumPreferences;
@Getter
private final VContext mVContext;
public Options getSavedOptions() {
return VOptionPreferenceUtils.getOptionsFromPreferences(mVanadiumPreferences);
}
private VContext vinit() {
try {
return V.init(mAndroidContext, getSavedOptions());
} catch (final RuntimeException e) {
if (mVanadiumPreferences.getAll().isEmpty()) {
throw e;
} else {
mErrorReporter.onError(R.string.err_vinit_options, e);
// Don't actually clear/fix options here; leave that to the user
return V.init(mAndroidContext);
}
}
}
public VAndroidContextMixin(final T androidContext, final BlessingsProvider blessingsProvider,
final ErrorReporter errorReporter) {
this(androidContext, (x, y) -> blessingsProvider, errorReporter);
}
public VAndroidContextMixin(
final T androidContext, final BiFunction<? super VContext, ? super T, BlessingsProvider>
blessingsProviderFactory, final ErrorReporter errorReporter) {
mAndroidContext = androidContext;
mErrorReporter = errorReporter;
mVanadiumPreferences = getVanadiumPreferences(mAndroidContext);
mVContext = vinit();
mBlessingsProvider = blessingsProviderFactory.apply(mVContext, mAndroidContext);
//Any time our blessings change, we need to attach them to our principal.
mBlessingsProvider.getPassiveRxBlessings().subscribe(this::processBlessings,
t -> errorReporter.onError(R.string.err_blessings_misc, t));
}
@Override
public void close() {
mVContext.cancel();
}
protected void processBlessings(final Blessings blessings) {
try {
BlessingsUtils.assumeBlessings(mVContext, blessings);
} catch (final VException e) {
mErrorReporter.onError(R.string.err_blessings_assume, e);
}
}
public static <T extends Activity> VAndroidContextMixin<T> withDefaults(
final T activity, final Bundle savedInstanceState) {
final FragmentManager mgr = activity.getFragmentManager();
final ErrorReporterFragment errorReporter;
if (savedInstanceState == null) {
errorReporter = new ErrorReporterFragment();
final FragmentTransaction t = mgr.beginTransaction()
.add(errorReporter, ErrorReporterFragment.TAG);
if (DebugUtils.isApkDebug(activity)) {
log.info("Debug menu enabled");
t.add(new DebugFragment(), null);
}
t.commit();
} else {
errorReporter = ErrorReporterFragment.find(mgr);
}
return new VAndroidContextMixin<>(activity, BlessingsManagerBlessingsProvider::new,
errorReporter);
}
}