| // 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.content.Context; |
| import android.content.SharedPreferences; |
| import android.os.Bundle; |
| |
| import io.v.android.v23.V; |
| import io.v.v23.OptionDefs; |
| import io.v.v23.Options; |
| import io.v.v23.context.VContext; |
| import io.v.v23.security.Blessings; |
| import io.v.v23.verror.VException; |
| import lombok.Getter; |
| import lombok.experimental.Accessors; |
| import lombok.extern.slf4j.Slf4j; |
| |
| /** |
| * Android context trait incorporating common Vanadium utilities: |
| * <ul> |
| * <li>Vanadium initialization during {@code onCreate}; context available via |
| * {@code getVContext}</li> |
| * <li>Blessings management, available via {@code getBlessingsProvider().getRxBlessings}. |
| * Upon {@code subscribe}, blessings are refreshed from the {@code BlessingsManager} or sought from |
| * the {@code BlessingsProvider} (by default, the Account Manager).</li> |
| * </ul> |
| * <p> |
| * 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 VAndroidContextTrait<T extends Context> { |
| public static final String VANADIUM_OPTIONS_SHARED_PREFS = "VanadiumOptions"; |
| |
| 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) { |
| try { |
| /* V.shutdown so we might try V.init again if warranted. If we don't V.shutdown |
| first, the process can abruptly die. It seems that if this happens, Android might |
| just restart the app immediately, i.e. before we've been able to display an |
| intelligible error message. */ |
| V.shutdown(); |
| } catch (final RuntimeException e2) { |
| log.error("Unable to clean up failed Vanadium initialization", e2); |
| e.addSuppressed(e2); |
| } |
| |
| if (mVanadiumPreferences.getAll().isEmpty()) { |
| throw e; |
| } else { |
| mErrorReporter.reportError(R.string.err_vinit_options, e); |
| // Don't actually clear/fix options here; leave that to the user |
| return V.init(mAndroidContext); |
| } |
| } |
| } |
| |
| public VAndroidContextTrait(final T androidContext, final BlessingsProvider blessingsProvider, |
| final ErrorReporter errorReporter) { |
| mAndroidContext = androidContext; |
| mBlessingsProvider = blessingsProvider; |
| mErrorReporter = errorReporter; |
| |
| mVanadiumPreferences = getVanadiumPreferences(mAndroidContext); |
| mVContext = vinit(); |
| |
| //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)); |
| } |
| |
| 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> VAndroidContextTrait<T> withDefaults( |
| final T activity, final Bundle savedInstanceState) { |
| if (DebugUtils.isApkDebug(activity) && savedInstanceState == null) { |
| log.info("Debug menu enabled"); |
| activity.getFragmentManager().beginTransaction() |
| .add(new DebugFragment(), null) |
| .commit(); |
| } |
| final ErrorReporter errorReporter = new ErrorReporter(activity); |
| final BlessingsProvider blessingsProvider = |
| new BlessedActivityTrait(activity, errorReporter); |
| return new VAndroidContextTrait<>(activity, blessingsProvider, errorReporter); |
| } |
| } |