| package io.v.todos.sharing; |
| |
| |
| import android.app.Fragment; |
| import android.content.SharedPreferences; |
| import android.os.Bundle; |
| import android.preference.PreferenceManager; |
| import android.support.annotation.NonNull; |
| import android.support.annotation.Nullable; |
| import android.view.Menu; |
| import android.view.MenuInflater; |
| import android.view.MenuItem; |
| |
| import com.google.common.util.concurrent.FutureCallback; |
| import com.google.common.util.concurrent.Futures; |
| |
| import io.v.todos.R; |
| import io.v.todos.persistence.syncbase.SyncbasePersistence; |
| import io.v.v23.context.VContext; |
| import io.v.v23.discovery.Advertisement; |
| import io.v.v23.verror.VException; |
| |
| /** |
| * A fragment encapsulating menu options and functionality related to list sharing. |
| */ |
| public class NeighborhoodFragment extends Fragment |
| implements SharedPreferences.OnSharedPreferenceChangeListener { |
| public static final String |
| FRAGMENT_TAG = NeighborhoodFragment.class.getSimpleName(); |
| |
| private static final String PREF_ADVERTISE_NEIGHBORHOOD = "advertise neighborhood"; |
| |
| private static SharedPreferences sPrefs; |
| private static VContext sAdvertiseContext; |
| private static Advertisement sAd; |
| |
| // This has to be a field because registerOnSharedPreferenceChangeListener does not keep a hard |
| // reference to the listener, making it otherwise susceptible to garbage collection. |
| private static final SharedPreferences.OnSharedPreferenceChangeListener sSharedPrefListener = |
| new SharedPreferences.OnSharedPreferenceChangeListener() { |
| @Override |
| public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, |
| String key) { |
| if (PREF_ADVERTISE_NEIGHBORHOOD.equals(key)) { |
| updateSharePresence(); |
| } |
| } |
| }; |
| |
| /** |
| * Initializes advertisement of one's presence to the neighborhood and watches shared |
| * preferences to toggle sharing on and off as the preference is changed. |
| */ |
| public static void initSharePresence() { |
| sPrefs = sPrefs != null ? sPrefs : PreferenceManager.getDefaultSharedPreferences |
| (SyncbasePersistence.getAppContext()); |
| |
| sAd = new Advertisement(); |
| sAd.setInterfaceName(Sharing.getPresenceInterface()); |
| // TODO(alexfandrianto): Revisit why we must put an address inside the advertisement. |
| // If we are advertising our presence, then there isn't any need for it. |
| // For now, put our email address in the addresses, despite it being an attribute. |
| sAd.getAddresses().add(SyncbasePersistence.getPersonalEmail()); |
| |
| sPrefs.registerOnSharedPreferenceChangeListener(sSharedPrefListener); |
| updateSharePresence(); |
| } |
| |
| private static void updateSharePresence() { |
| if (isAdvertising()) { |
| if (sAdvertiseContext == null) { |
| sAdvertiseContext = SyncbasePersistence.getAppVContext().withCancel(); |
| try { |
| Futures.addCallback(Sharing.getDiscovery().advertise(sAdvertiseContext, sAd, |
| // TODO(rosswang): Restrict to contacts only. However, per discussion |
| // with mattr@ and ashankar@, this could be a scalability challenge as |
| // the advertisement would need to be IB-encrypted for each possible |
| // recipient. This can be broken into multiple advertisements but at the |
| // increased risk of over-the-air hash collision. |
| null), |
| new FutureCallback<Void>() { |
| @Override |
| public void onSuccess(@Nullable Void result) { |
| } |
| |
| @Override |
| public void onFailure(@NonNull Throwable t) { |
| handleAdvertisingError(t); |
| } |
| }); |
| } catch (VException e) { |
| handleAdvertisingError(e); |
| } |
| } |
| } else if (sAdvertiseContext != null) { |
| sAdvertiseContext.cancel(); |
| sAdvertiseContext = null; |
| } |
| } |
| |
| private static void handleAdvertisingError(Throwable t) { |
| SyncbasePersistence.getAppErrorReporter().onError(R.string.err_share_location, t); |
| setAdvertiseNeighborhood(false); |
| } |
| |
| private static void setAdvertiseNeighborhood(boolean value) { |
| sPrefs.edit() |
| .putBoolean(PREF_ADVERTISE_NEIGHBORHOOD, value) |
| .apply(); |
| } |
| |
| private static boolean isAdvertising() { |
| return sPrefs.getBoolean(PREF_ADVERTISE_NEIGHBORHOOD, true); |
| } |
| |
| private MenuItem mAdvertiseNeighborhoodMenuItem; |
| |
| @Override |
| public void onCreate(@Nullable Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setHasOptionsMenu(true); |
| } |
| |
| @Override |
| public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { |
| inflater.inflate(R.menu.neighborhood, menu); |
| mAdvertiseNeighborhoodMenuItem = menu.findItem(R.id.advertise_neighborhood); |
| sPrefs = sPrefs != null ? sPrefs : PreferenceManager.getDefaultSharedPreferences |
| (getActivity()); |
| sPrefs.registerOnSharedPreferenceChangeListener(this); |
| updateAdvertiseNeighborhoodChecked(); |
| } |
| |
| @Override |
| public void onDestroyOptionsMenu() { |
| sPrefs.unregisterOnSharedPreferenceChangeListener(this); |
| } |
| |
| @Override |
| public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { |
| if (PREF_ADVERTISE_NEIGHBORHOOD.equals(key)) { |
| updateAdvertiseNeighborhoodChecked(); |
| } |
| } |
| |
| private void updateAdvertiseNeighborhoodChecked() { |
| boolean value = isAdvertising(); |
| mAdvertiseNeighborhoodMenuItem.setChecked(value); |
| mAdvertiseNeighborhoodMenuItem.setIcon(value ? |
| R.drawable.ic_advertise_neighborhood_on_white_24dp : |
| R.drawable.ic_advertise_neighborhood_off_white_24dp); |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| if (item.getItemId() == R.id.advertise_neighborhood) { |
| setAdvertiseNeighborhood(!item.isChecked()); |
| return true; |
| } else { |
| return super.onOptionsItemSelected(item); |
| } |
| } |
| } |