diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/Role.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/Role.java new file mode 100644 index 0000000000..4ba1eff812 --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/Role.java @@ -0,0 +1,23 @@ +package com.github.dedis.popstellar.model; + +import androidx.annotation.StringRes; + +import com.github.dedis.popstellar.R; + +public enum Role { + ORGANIZER(R.string.organizer), + WITNESS(R.string.witness), + ATTENDEE(R.string.attendee), + MEMBER(R.string.member); + + @StringRes private final int roleString; + + Role(@StringRes int role) { + this.roleString = role; + } + + @StringRes + public int getStringId() { + return roleString; + } +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/IdentityFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/IdentityFragment.java deleted file mode 100644 index bfe49b324e..0000000000 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/IdentityFragment.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.github.dedis.popstellar.ui.detail; - -import android.graphics.Bitmap; -import android.os.Bundle; -import android.view.*; -import android.widget.*; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - -import com.github.dedis.popstellar.R; -import com.github.dedis.popstellar.model.objects.security.PublicKey; -import com.github.dedis.popstellar.model.qrcode.MainPublicKeyData; -import com.google.gson.Gson; - -import net.glxn.qrgen.android.QRCode; - -import javax.inject.Inject; - -import dagger.hilt.android.AndroidEntryPoint; - -/** - * Represents the identity of a user within an organization (which allows users to “wear different - * hats” in different organizations) TODO : For the moment, the goal of this UI is just to show a QR - * code, but in the future, it will be needed to store identity information somewhere to make it - * dependent of the current user and LAO - */ -@AndroidEntryPoint -public class IdentityFragment extends Fragment { - - public static final String TAG = IdentityFragment.class.getSimpleName(); - - @Inject Gson gson; - - public static final String PUBLIC_KEY = "public key"; - private EditText identityNameEditText; - private EditText identityTitleEditText; - private EditText identityOrganizationEditText; - private EditText identityEmailEditText; - private EditText identityPhoneEditText; - private ImageView qrCode; - - public static IdentityFragment newInstance(PublicKey publicKey) { - IdentityFragment identityFragment = new IdentityFragment(); - Bundle bundle = new Bundle(1); - bundle.putString(PUBLIC_KEY, publicKey.getEncoded()); - identityFragment.setArguments(bundle); - return identityFragment; - } - - @Nullable - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.identity_fragment, container, false); - // TODO : The QR code does not appear at all unless the Name field is non-empty - // and not all whitespace. - CheckBox anonymousCheckBox = view.findViewById(R.id.checkbox_anonymous); - qrCode = view.findViewById(R.id.qr_code); - identityEmailEditText = view.findViewById(R.id.identity_email); - identityNameEditText = view.findViewById(R.id.identity_name); - identityOrganizationEditText = view.findViewById(R.id.identity_organization); - identityPhoneEditText = view.findViewById(R.id.identity_phone); - identityTitleEditText = view.findViewById(R.id.identity_title); - - hideIdentityInformation(); - - anonymousCheckBox.setOnCheckedChangeListener( - (buttonView, isChecked) -> { - if (isChecked) { - hideIdentityInformation(); - } else { - qrCode.setVisibility(View.VISIBLE); - identityEmailEditText.setVisibility(View.VISIBLE); - identityNameEditText.setVisibility(View.VISIBLE); - identityOrganizationEditText.setVisibility(View.VISIBLE); - identityPhoneEditText.setVisibility(View.VISIBLE); - identityTitleEditText.setVisibility(View.VISIBLE); - } - }); - - // for now we use the user's public key to generate the QR code - // TODO: In the future use Wallet with user's token - String pk = this.requireArguments().getString(PUBLIC_KEY); - identityNameEditText.setText(pk); - - MainPublicKeyData data = new MainPublicKeyData(new PublicKey(pk)); - Bitmap myBitmap = QRCode.from(gson.toJson(data)).bitmap(); - qrCode.setImageBitmap(myBitmap); - - return view; - } - - @Override - public void onResume() { - super.onResume(); - LaoDetailViewModel viewModel = LaoDetailActivity.obtainViewModel(requireActivity()); - viewModel.setPageTitle(getString(R.string.tab_identity)); - } - - /** Hide fields when user wants to be anonymous */ - private void hideIdentityInformation() { - qrCode.setVisibility(View.GONE); - identityEmailEditText.setVisibility(View.GONE); - identityNameEditText.setVisibility(View.GONE); - identityOrganizationEditText.setVisibility(View.GONE); - identityPhoneEditText.setVisibility(View.GONE); - identityTitleEditText.setVisibility(View.GONE); - } -} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/InviteFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/InviteFragment.java new file mode 100644 index 0000000000..751ea71e70 --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/InviteFragment.java @@ -0,0 +1,81 @@ +package com.github.dedis.popstellar.ui.detail; + +import android.graphics.Bitmap; +import android.os.Bundle; +import android.view.*; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.github.dedis.popstellar.R; +import com.github.dedis.popstellar.databinding.InviteFragmentBinding; +import com.github.dedis.popstellar.model.objects.view.LaoView; +import com.github.dedis.popstellar.model.qrcode.ConnectToLao; +import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager; +import com.github.dedis.popstellar.utility.error.ErrorUtils; +import com.github.dedis.popstellar.utility.error.UnknownLaoException; +import com.google.gson.Gson; + +import net.glxn.qrgen.android.QRCode; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; + +@AndroidEntryPoint +public class InviteFragment extends Fragment { + + private static final String TAG = InviteFragment.class.getSimpleName(); + + @Inject Gson gson; + @Inject GlobalNetworkManager networkManager; + + private static final int QR_SIDE = 800; + + private LaoDetailViewModel viewModel; + + public static InviteFragment newInstance() { + return new InviteFragment(); + } + + @Nullable + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + InviteFragmentBinding binding = InviteFragmentBinding.inflate(inflater, container, false); + viewModel = LaoDetailActivity.obtainViewModel(requireActivity()); + + binding.laoPropertiesIdentifierText.setText(viewModel.getPublicKey().getEncoded()); + binding.laoPropertiesServerText.setText(networkManager.getCurrentUrl()); + + try { + LaoView laoView = viewModel.getLao(); + + ConnectToLao data = new ConnectToLao(networkManager.getCurrentUrl(), laoView.getId()); + Bitmap myBitmap = QRCode.from(gson.toJson(data)).withSize(QR_SIDE, QR_SIDE).bitmap(); + binding.channelQrCode.setImageBitmap(myBitmap); + binding.laoPropertiesNameText.setText(laoView.getName()); + + viewModel + .getRole() + .observe( + getViewLifecycleOwner(), + role -> binding.laoPropertiesRoleText.setText(role.getStringId())); + + } catch (UnknownLaoException e) { + ErrorUtils.logAndShow(requireContext(), TAG, e, R.string.unknown_lao_exception); + return null; + } + return binding.getRoot(); + } + + @Override + public void onResume() { + super.onResume(); + viewModel.setPageTitle(R.string.invite); + viewModel.setIsTab(true); + } +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailActivity.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailActivity.java index 1c3958c594..53e2ad7afd 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailActivity.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailActivity.java @@ -2,36 +2,29 @@ import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; import android.os.Bundle; import android.util.Log; -import android.view.View; import android.widget.Toast; -import androidx.annotation.*; +import androidx.annotation.IdRes; +import androidx.annotation.Nullable; import androidx.fragment.app.*; import androidx.lifecycle.ViewModelProvider; import com.github.dedis.popstellar.R; import com.github.dedis.popstellar.databinding.LaoDetailActivityBinding; -import com.github.dedis.popstellar.model.objects.view.LaoView; -import com.github.dedis.popstellar.model.qrcode.ConnectToLao; import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager; -import com.github.dedis.popstellar.ui.detail.event.LaoDetailAnimation; import com.github.dedis.popstellar.ui.detail.token.TokenListFragment; import com.github.dedis.popstellar.ui.detail.witness.WitnessingFragment; import com.github.dedis.popstellar.ui.digitalcash.DigitalCashActivity; import com.github.dedis.popstellar.ui.home.HomeActivity; -import com.github.dedis.popstellar.ui.navigation.NavigationActivity; +import com.github.dedis.popstellar.ui.navigation.LaoActivity; +import com.github.dedis.popstellar.ui.navigation.MainMenuTab; import com.github.dedis.popstellar.ui.socialmedia.SocialMediaActivity; import com.github.dedis.popstellar.utility.ActivityUtils; import com.github.dedis.popstellar.utility.Constants; -import com.github.dedis.popstellar.utility.error.ErrorUtils; -import com.github.dedis.popstellar.utility.error.UnknownLaoException; import com.google.gson.Gson; -import net.glxn.qrgen.android.QRCode; - import java.security.GeneralSecurityException; import java.util.Objects; import java.util.function.Supplier; @@ -41,12 +34,11 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint -public class LaoDetailActivity extends NavigationActivity { +public class LaoDetailActivity extends LaoActivity { private static final String TAG = LaoDetailActivity.class.getSimpleName(); private LaoDetailViewModel viewModel; - private LaoDetailActivityBinding binding; @Inject Gson gson; @Inject GlobalNetworkManager networkManager; @@ -54,17 +46,26 @@ public class LaoDetailActivity extends NavigationActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = LaoDetailActivityBinding.inflate(getLayoutInflater()); + LaoDetailActivityBinding binding = LaoDetailActivityBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - navigationViewModel = viewModel = obtainViewModel(this); - - setupNavigationBar(findViewById(R.id.lao_detail_nav_bar)); - setupTopAppBar(); - setupProperties(); + laoViewModel = viewModel = obtainViewModel(this); String laoId = Objects.requireNonNull(getIntent().getExtras()).getString(Constants.LAO_ID_EXTRA); viewModel.subscribeToLao(laoId); + + initializeLaoActivity( + laoId, + binding.laoDetailNavigationDrawer, + binding.laoTopAppBar, + binding.laoDetailDrawerLayout); + + MainMenuTab tab = (MainMenuTab) getIntent().getExtras().get(Constants.TAB_EXTRA); + if (tab == null) { + tab = MainMenuTab.EVENTS; + } + laoViewModel.setCurrentTab(tab); + openTab(tab); } @Override @@ -80,89 +81,17 @@ public void onStop() { } } - private void setupTopAppBar() { - viewModel.getPageTitle().observe(this, binding.laoTopAppBar::setTitle); - - binding.laoTopAppBar.setNavigationOnClickListener( - v -> { - Fragment fragment = - getSupportFragmentManager().findFragmentById(R.id.fragment_container_lao_detail); - if (fragment instanceof LaoDetailFragment) { - startActivity(HomeActivity.newIntent(this)); - } else { - if (viewModel.getCurrentTab().getValue() == LaoTab.EVENTS) { - // On reselection the navigation is supposed to do nothing to prevent loops, so we - // manually change the fragment - openEventsTab(); - } else { - viewModel.setCurrentTab(LaoTab.EVENTS); - } - } - }); - binding.laoTopAppBar.setOnMenuItemClickListener( - item -> { - if (item.getItemId() == R.id.lao_toolbar_qr_code) { - binding.laoDetailQrLayout.setVisibility(View.VISIBLE); - binding.laoDetailQrLayout.setAlpha(Constants.ENABLED_ALPHA); - binding.fragmentContainerLaoDetail.setAlpha(Constants.DISABLED_ALPHA); - binding.fragmentContainerLaoDetail.setEnabled(false); - return true; - } - return false; - }); - } - - private void setupProperties() { - binding.laoPropertiesIdentifierText.setText(viewModel.getPublicKey().getEncoded()); - binding.laoPropertiesServerText.setText(networkManager.getCurrentUrl()); - - viewModel - .getCurrentLao() - .observe( - this, - laoView -> { - // Set the QR code - ConnectToLao data = new ConnectToLao(networkManager.getCurrentUrl(), laoView.getId()); - Bitmap myBitmap = - QRCode.from(gson.toJson(data)) - .withSize(Constants.QR_SIDE, Constants.QR_SIDE) - .bitmap(); - binding.channelQrCode.setImageBitmap(myBitmap); - - binding.laoPropertiesNameText.setText(laoView.getName()); - binding.laoPropertiesRoleText.setText(getRole()); - }); - - binding.qrIconClose.setOnClickListener( - v -> { - LaoDetailAnimation.fadeOut(binding.laoDetailQrLayout, 1.0f, 0.0f, 500); - binding.fragmentContainerLaoDetail.setAlpha(Constants.ENABLED_ALPHA); - binding.fragmentContainerLaoDetail.setEnabled(true); - }); - } - - @StringRes - private int getRole() { - if (Boolean.TRUE.equals(viewModel.isOrganizer().getValue())) { - return R.string.organizer; - } else { - try { - boolean isWitness = Boolean.TRUE.equals(viewModel.isWitness().getValue()); - return isWitness ? R.string.witness : R.string.attendee; - } catch (UnknownLaoException e) { - return 0; - } - } - } - @Override - protected LaoTab findTabByMenu(int menuId) { - return LaoTab.findByMenu(menuId); + public void onBackPressed() { + openEventsTab(); } @Override - protected boolean openTab(LaoTab tab) { + protected boolean openTab(MainMenuTab tab) { switch (tab) { + case INVITE: + openInviteTab(); + return true; case EVENTS: openEventsTab(); return true; @@ -175,18 +104,21 @@ protected boolean openTab(LaoTab tab) { case DIGITAL_CASH: openDigitalCashTab(); return false; - case SOCIAL: + case SOCIAL_MEDIA: openSocialMediaTab(); return false; + case DISCONNECT: + startActivity(HomeActivity.newIntent(this)); + return false; default: Log.w(TAG, "Unhandled tab type : " + tab); return false; } } - @Override - protected LaoTab getDefaultTab() { - return LaoTab.EVENTS; + private void openInviteTab() { + setCurrentFragment( + getSupportFragmentManager(), R.id.fragment_invite, InviteFragment::newInstance); } private void openEventsTab() { @@ -209,12 +141,7 @@ private void openDigitalCashTab() { } private void openSocialMediaTab() { - try { - LaoView laoView = viewModel.getLaoView(); - startActivity(SocialMediaActivity.newIntent(this, viewModel.getLaoId(), laoView.getName())); - } catch (UnknownLaoException e) { - ErrorUtils.logAndShow(this, TAG, R.string.error_no_lao); - } + startActivity(SocialMediaActivity.newIntent(this, viewModel.getLaoId())); } public static LaoDetailViewModel obtainViewModel(FragmentActivity activity) { @@ -240,6 +167,12 @@ public static Intent newIntentForLao(Context ctx, String laoId) { return intent; } + public static Intent newIntentWithTab(Context ctx, String laoId, MainMenuTab tab) { + Intent intent = LaoDetailActivity.newIntentForLao(ctx, laoId); + intent.putExtra(Constants.TAB_EXTRA, tab); + return intent; + } + public static Intent newIntentForWallet(Context ctx, String laoId) { Intent intent = new Intent(ctx, LaoDetailActivity.class); intent.putExtra(Constants.LAO_ID_EXTRA, laoId); diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailFragment.java index 260cecaecd..137e556804 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailFragment.java @@ -13,12 +13,11 @@ import com.github.dedis.popstellar.R; import com.github.dedis.popstellar.databinding.LaoDetailFragmentBinding; +import com.github.dedis.popstellar.model.Role; import com.github.dedis.popstellar.model.objects.event.EventType; -import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager; import com.github.dedis.popstellar.ui.detail.event.*; import com.github.dedis.popstellar.ui.detail.event.election.fragments.ElectionSetupFragment; import com.github.dedis.popstellar.ui.detail.event.rollcall.RollCallCreationFragment; -import com.github.dedis.popstellar.utility.error.UnknownLaoException; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.gson.Gson; @@ -33,7 +32,6 @@ public class LaoDetailFragment extends Fragment { public static final String TAG = LaoDetailFragment.class.getSimpleName(); @Inject Gson gson; - @Inject GlobalNetworkManager networkManager; private LaoDetailFragmentBinding binding; private LaoDetailViewModel viewModel; @@ -52,11 +50,16 @@ public View onCreateView( binding = LaoDetailFragmentBinding.inflate(inflater, container, false); viewModel = LaoDetailActivity.obtainViewModel(requireActivity()); - binding.setViewModel(viewModel); binding.setLifecycleOwner(requireActivity()); FloatingActionButton addButton = binding.addEvent; addButton.setOnClickListener(fabListener); + viewModel + .getRole() + .observe( + requireActivity(), + role -> + addButton.setVisibility(role.equals(Role.ORGANIZER) ? View.VISIBLE : View.GONE)); binding.addElection.setOnClickListener(openCreateEvent(EventType.ELECTION)); binding.addElectionText.setOnClickListener(openCreateEvent(EventType.ELECTION)); @@ -115,11 +118,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat @Override public void onResume() { super.onResume(); - try { - viewModel.setPageTitle(viewModel.getLaoView().getName()); - } catch (UnknownLaoException e) { - Log.d(TAG, "Lao name could not be retrieved"); - } + viewModel.setPageTitle(R.string.event_list); + viewModel.setIsTab(true); } private void setupEventListAdapter() { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailViewModel.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailViewModel.java index 1e8f626457..b80fb5af0a 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailViewModel.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoDetailViewModel.java @@ -5,7 +5,8 @@ import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.lifecycle.*; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import com.github.dedis.popstellar.R; import com.github.dedis.popstellar.SingleEvent; @@ -25,7 +26,7 @@ import com.github.dedis.popstellar.model.qrcode.PopTokenData; import com.github.dedis.popstellar.repository.*; import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager; -import com.github.dedis.popstellar.ui.navigation.NavigationViewModel; +import com.github.dedis.popstellar.ui.navigation.LaoViewModel; import com.github.dedis.popstellar.ui.qrcode.QRCodeScanningViewModel; import com.github.dedis.popstellar.ui.qrcode.ScanningAction; import com.github.dedis.popstellar.utility.ActivityUtils; @@ -49,12 +50,10 @@ import io.reactivex.*; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; @HiltViewModel -public class LaoDetailViewModel extends NavigationViewModel - implements QRCodeScanningViewModel { +public class LaoDetailViewModel extends LaoViewModel implements QRCodeScanningViewModel { public static final String TAG = LaoDetailViewModel.class.getSimpleName(); private static final String LAO_FAILURE_MESSAGE = "failed to retrieve current lao"; @@ -71,22 +70,8 @@ public class LaoDetailViewModel extends NavigationViewModel /* * LiveData objects that represent the state in a fragment */ - private final MutableLiveData mCurrentLao = new MutableLiveData<>(); - private final MutableLiveData mPageTitle = new MutableLiveData<>(); - private final MutableLiveData mIsOrganizer = new MutableLiveData<>(); - private final MutableLiveData mIsWitness = new MutableLiveData<>(); private final MutableLiveData mIsSignedByCurrentWitness = new MutableLiveData<>(); private final MutableLiveData mNbAttendees = new MutableLiveData<>(); - private final LiveData> mWitnesses = - Transformations.map( - mCurrentLao, - lao -> lao == null ? new ArrayList<>() : new ArrayList<>(lao.getWitnesses())); - - private final LiveData> mWitnessMessages = - Transformations.map( - mCurrentLao, - lao -> - lao == null ? new ArrayList<>() : new ArrayList<>(lao.getWitnessMessages().values())); private Observable> events; private Observable> attendedRollCalls; @@ -146,7 +131,7 @@ private boolean attendedOrOrganized(RollCall rollcall) { // find out if user has attended the rollcall try { PublicKey pk = wallet.generatePoPToken(laoId, rollcall.getPersistentId()).getPublicKey(); - return rollcall.getAttendees().contains(pk) || Boolean.TRUE.equals(isOrganizer().getValue()); + return rollcall.getAttendees().contains(pk) || isOrganizer(); } catch (KeyGenerationException | UninitializedWalletException e) { Log.e(TAG, "failed to retrieve public key from wallet", e); return false; @@ -192,7 +177,7 @@ public Completable openElection(Election e) { Log.d(TAG, "opening election with name : " + e.getName()); LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException unknownLaoException) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -213,7 +198,7 @@ public Completable endElection(Election election) { Log.d(TAG, "ending election with name : " + election.getName()); LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -241,6 +226,7 @@ public Completable sendVote(String electionId, List votes) { try { election = electionRepo.getElection(laoId, electionId); } catch (UnknownElectionException e) { + Log.d(TAG, "failed to retrieve current election"); return Completable.error(e); } @@ -253,7 +239,7 @@ public Completable sendVote(String electionId, List votes) { final LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -306,7 +292,7 @@ public Completable createNewElection( LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -345,7 +331,7 @@ public Single createNewRollCall( LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Single.error(new UnknownLaoException()); @@ -383,7 +369,7 @@ public Single sendConsensusElect( LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Single.error(new UnknownLaoException()); @@ -434,7 +420,7 @@ public Completable openRollCall(String id) { LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -485,7 +471,7 @@ public Completable closeRollCall() { LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -511,7 +497,7 @@ public Completable signMessage(WitnessMessage witnessMessage) { Log.d(TAG, "signing message with ID " + witnessMessage.getMessageId()); final LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -546,37 +532,11 @@ public Observable> getEvents() { return events; } - public LiveData getCurrentLao() { - return mCurrentLao; - } - - public String getLaoId() { - return laoId; - } - - public LaoView getLaoView() throws UnknownLaoException { + @Override + public LaoView getLao() throws UnknownLaoException { return laoRepository.getLaoView(laoId); } - public MutableLiveData getPageTitle() { - return mPageTitle; - } - - public void setPageTitle(String title) { - mPageTitle.postValue(title); - } - - public LiveData isOrganizer() { - return mIsOrganizer; - } - - public LiveData isWitness() throws UnknownLaoException { - boolean isWitness = getLaoView().getWitnesses().contains(keyManager.getMainPublicKey()); - Log.d(TAG, "isWitness: " + isWitness); - mIsWitness.setValue(isWitness); - return mIsWitness; - } - public LiveData isSignedByCurrentWitness(Set witnesses) { boolean isSignedByCurrentWitness = witnesses.contains(keyManager.getMainPublicKey()); Log.d(TAG, "isSignedByCurrentWitness: " + isSignedByCurrentWitness); @@ -584,22 +544,15 @@ public LiveData isSignedByCurrentWitness(Set witnesses) { return mIsSignedByCurrentWitness; } - public LiveData> getWitnesses() { - return mWitnesses; - } - - public LiveData> getWitnessMessages() { - return mWitnessMessages; - } - public LiveData getNbAttendees() { return mNbAttendees; } public Observable> getNodes() throws UnknownLaoException { - return laoRepository.getNodesByChannel(getLaoView().getChannel()); + return laoRepository.getNodesByChannel(getLao().getChannel()); } + public LiveData> getAttendeeScanConfirmEvent() { return mAttendeeScanConfirmEvent; } @@ -629,7 +582,7 @@ public Completable updateLaoWitnesses() { LaoView laoView; try { - laoView = getLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.d(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -706,26 +659,10 @@ public void subscribeToLao(String laoId) { .throttleLatest(50, TimeUnit.MILLISECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); - - disposables.add( - laoRepository - .getLaoObservable(laoId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - laoView -> { - Log.d(TAG, "got an update for lao: " + laoView.getName()); - - mCurrentLao.postValue(laoView); - boolean isOrganizer = - laoView.getOrganizer().equals(keyManager.getMainPublicKey()); - mIsOrganizer.setValue(isOrganizer); - }, - error -> Log.d(TAG, "error updating LAO :" + error))); } public PoPToken getCurrentPopToken(RollCall rollCall) throws KeyException, UnknownLaoException { - return keyManager.getPoPToken(getLaoView(), rollCall); + return keyManager.getPoPToken(getLao(), rollCall); } public boolean isWalletSetup() { @@ -830,17 +767,4 @@ public boolean handleWitnessAddition(String data) { return true; } - - /** - * This function should be used to add disposable object generated from subscription to sent - * messages flows - * - *

They will be disposed of when the view model is cleaned which ensures that the subscription - * stays relevant throughout the whole lifecycle of the activity and it is not bound to a fragment - * - * @param disposable to add - */ - public void addDisposable(Disposable disposable) { - this.disposables.add(disposable); - } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoTab.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoTab.java deleted file mode 100644 index 724e1581db..0000000000 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/LaoTab.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.github.dedis.popstellar.ui.detail; - -import androidx.annotation.IdRes; - -import com.github.dedis.popstellar.R; -import com.github.dedis.popstellar.ui.navigation.Tab; - -import java.util.Arrays; -import java.util.List; - -/** Enum where each element represent a tab in LaoActivity */ -public enum LaoTab implements Tab { - EVENTS(R.id.lao_detail_event_list_menu), - TOKENS(R.id.lao_detail_tokens_menu), - WITNESSING(R.id.lao_detail_witnessing_menu), - DIGITAL_CASH(R.id.lao_detail_digital_cash_menu), - SOCIAL(R.id.lao_detail_social_media_menu); - - private static final List ALL = Arrays.asList(values()); - - /** - * Find a tab based on its menu id, throws an exception when no tab match the id - * - * @param menuId of the menu - * @return the tab corresponding to the given menu id - */ - public static LaoTab findByMenu(@IdRes int menuId) { - for (LaoTab tab : ALL) { - if (tab.menuId == menuId) { - return tab; - } - } - - throw new IllegalArgumentException("Unknown id : " + menuId); - } - - @IdRes private final int menuId; - - LaoTab(@IdRes int menuId) { - this.menuId = menuId; - } - - @IdRes - @Override - public int getMenuId() { - return menuId; - } - - @Override - public String getName() { - return toString(); - } -} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/consensus/ElectionStartFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/consensus/ElectionStartFragment.java index 10e16190d0..f28566aff7 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/consensus/ElectionStartFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/consensus/ElectionStartFragment.java @@ -1,8 +1,12 @@ package com.github.dedis.popstellar.ui.detail.event.consensus; +import static io.reactivex.android.schedulers.AndroidSchedulers.mainThread; + import android.os.Bundle; import android.util.Log; -import android.view.*; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import android.widget.GridView; import androidx.annotation.NonNull; @@ -10,26 +14,31 @@ import com.github.dedis.popstellar.R; import com.github.dedis.popstellar.databinding.ElectionStartFragmentBinding; -import com.github.dedis.popstellar.model.objects.*; +import com.github.dedis.popstellar.model.objects.ConsensusNode; +import com.github.dedis.popstellar.model.objects.ElectInstance; import com.github.dedis.popstellar.model.objects.ElectInstance.State; +import com.github.dedis.popstellar.model.objects.Election; import com.github.dedis.popstellar.model.objects.view.LaoView; import com.github.dedis.popstellar.repository.ElectionRepository; import com.github.dedis.popstellar.ui.detail.LaoDetailActivity; import com.github.dedis.popstellar.ui.detail.LaoDetailViewModel; -import com.github.dedis.popstellar.utility.error.*; - -import java.text.SimpleDateFormat; -import java.time.Instant; -import java.util.*; - -import javax.inject.Inject; +import com.github.dedis.popstellar.utility.error.ErrorUtils; +import com.github.dedis.popstellar.utility.error.UnknownElectionException; +import com.github.dedis.popstellar.utility.error.UnknownLaoException; import dagger.hilt.android.AndroidEntryPoint; import io.reactivex.Observable; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.functions.Consumer; -import static io.reactivex.android.schedulers.AndroidSchedulers.mainThread; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Optional; + +import javax.inject.Inject; /** * A simple {@link Fragment} subclass. Use the {@link ElectionStartFragment#newInstance} factory @@ -101,7 +110,7 @@ public View onCreateView( setupButtonListeners(viewModel, electionId); try { - LaoView laoView = viewModel.getLaoView(); + LaoView laoView = viewModel.getLao(); ownNode = laoView.getNode(viewModel.getPublicKey()); if (ownNode == null) { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/CastVoteFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/CastVoteFragment.java index 34f4158e81..58c891f72e 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/CastVoteFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/CastVoteFragment.java @@ -107,7 +107,7 @@ public View onCreateView( private boolean setLaoName() { try { - LaoView laoView = viewModel.getLaoView(); + LaoView laoView = viewModel.getLao(); binding.castVoteLaoName.setText(laoView.getName()); return false; } catch (UnknownLaoException e) { @@ -176,6 +176,7 @@ private void castVote(View voteButton) { @Override public void onResume() { super.onResume(); - viewModel.setPageTitle(getString(R.string.vote)); + viewModel.setPageTitle(R.string.vote); + viewModel.setIsTab(false); } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionFragment.java index c49c74dbf4..1a61f9e078 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionFragment.java @@ -206,7 +206,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat @Override public void onResume() { super.onResume(); - viewModel.setPageTitle(getString(R.string.election_title)); + viewModel.setPageTitle(R.string.election_title); + viewModel.setIsTab(false); } @Override @@ -350,8 +351,7 @@ private EnumMap buildActionTextMap() { private EnumMap buildManagementVisibilityMap() { // Only the organizer may start or end an election - int organizerVisibility = - Boolean.TRUE.equals(viewModel.isOrganizer().getValue()) ? View.VISIBLE : View.GONE; + int organizerVisibility = viewModel.isOrganizer() ? View.VISIBLE : View.GONE; EnumMap map = new EnumMap<>(EventState.class); map.put(EventState.CREATED, organizerVisibility); diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionResultFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionResultFragment.java index 0be42667cc..3da5498b06 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionResultFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionResultFragment.java @@ -56,7 +56,7 @@ public View onCreateView( String electionId = requireArguments().getString(ELECTION_ID); try { - LaoView laoView = viewModel.getLaoView(); + LaoView laoView = viewModel.getLao(); Election election = electionRepository.getElection(viewModel.getLaoId(), electionId); // Setting the Lao Name @@ -88,6 +88,7 @@ public View onCreateView( public void onResume() { super.onResume(); LaoDetailViewModel viewModel = LaoDetailActivity.obtainViewModel(requireActivity()); - viewModel.setPageTitle(getString(R.string.election_result_title)); + viewModel.setPageTitle(R.string.election_result_title); + viewModel.setIsTab(false); } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionSetupFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionSetupFragment.java index f31f5d7bb1..f48ca2e60f 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionSetupFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/election/fragments/ElectionSetupFragment.java @@ -186,7 +186,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat @Override public void onResume() { super.onResume(); - viewModel.setPageTitle(getString(R.string.election_setup_title)); + viewModel.setPageTitle(R.string.election_setup_title); + viewModel.setIsTab(false); } /** Setups the submit button that creates the new election */ diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/rollcall/RollCallCreationFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/rollcall/RollCallCreationFragment.java index c31a93425a..3065f26b95 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/rollcall/RollCallCreationFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/rollcall/RollCallCreationFragment.java @@ -104,7 +104,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat @Override public void onResume() { super.onResume(); - viewModel.setPageTitle(getString(R.string.roll_call_setup_title)); + viewModel.setPageTitle(R.string.roll_call_setup_title); + viewModel.setIsTab(false); } private void setupConfirmButton() { @@ -145,7 +146,7 @@ private void createRollCall(boolean open) { getParentFragmentManager(), R.id.fragment_qrcode, QRCodeScanningFragment::new); - viewModel.setPageTitle(getString(R.string.add_attendee_title)); + viewModel.setPageTitle(R.string.add_attendee_title); }, error -> ErrorUtils.logAndShow( @@ -153,13 +154,11 @@ private void createRollCall(boolean open) { } else { viewModel.addDisposable( createRollCall.subscribe( - id -> { - setCurrentFragment( - getParentFragmentManager(), - R.id.fragment_lao_detail, - LaoDetailFragment::newInstance); - viewModel.setPageTitle(viewModel.getLaoView().getName()); - }, + id -> + setCurrentFragment( + getParentFragmentManager(), + R.id.fragment_lao_detail, + LaoDetailFragment::newInstance), error -> ErrorUtils.logAndShow( requireContext(), TAG, error, R.string.error_create_rollcall))); diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/rollcall/RollCallFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/rollcall/RollCallFragment.java index 43955bd06d..b1a105aec4 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/rollcall/RollCallFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/event/rollcall/RollCallFragment.java @@ -154,7 +154,8 @@ public View onCreateView( @Override public void onResume() { super.onResume(); - viewModel.setPageTitle(getString(R.string.roll_call_title)); + viewModel.setPageTitle(R.string.roll_call_title); + viewModel.setIsTab(false); try { rollCall = viewModel.getRollCall(requireArguments().getString(ROLL_CALL_ID)); } catch (UnknownRollCallException e) { @@ -166,7 +167,7 @@ private void setUpStateDependantContent() { setupTime(); // Suggested time is updated in case of early/late close/open/reopen EventState rcState = rollCall.getState(); - boolean isOrganizer = Boolean.TRUE.equals(viewModel.isOrganizer().getValue()); + boolean isOrganizer = viewModel.isOrganizer(); binding.rollCallFragmentTitle.setText(rollCall.getName()); binding.rollCallManagementButton.setVisibility(isOrganizer ? View.VISIBLE : View.GONE); @@ -221,8 +222,7 @@ private void retrieveAndDisplayPublicKey() { PopTokenData data = new PopTokenData(new PublicKey(pk)); Bitmap myBitmap = QRCode.from(gson.toJson(data)).bitmap(); binding.rollCallPkQrCode.setImageBitmap(myBitmap); - binding.rollCallPkQrCode.setVisibility( - Boolean.TRUE.equals(viewModel.isOrganizer().getValue()) ? View.INVISIBLE : View.VISIBLE); + binding.rollCallPkQrCode.setVisibility(viewModel.isOrganizer() ? View.INVISIBLE : View.VISIBLE); } private EnumMap buildManagementTextMap() { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/token/TokenFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/token/TokenFragment.java index 565f18ea10..73386a1772 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/token/TokenFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/token/TokenFragment.java @@ -58,7 +58,8 @@ public static TokenFragment newInstance(String rcId) { @Override public void onResume() { super.onResume(); - viewModel.setPageTitle(getString(R.string.token)); + viewModel.setPageTitle(R.string.token); + viewModel.setIsTab(false); } @Override diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/token/TokenListFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/token/TokenListFragment.java index fb04985185..0d1e5e9ec9 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/token/TokenListFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/token/TokenListFragment.java @@ -41,7 +41,8 @@ public TokenListFragment() { @Override public void onResume() { super.onResume(); - viewModel.setPageTitle(getString(R.string.tokens)); + viewModel.setPageTitle(R.string.tokens); + viewModel.setIsTab(true); } @Nullable diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessListAdapter.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessListAdapter.java index de8d3682a7..c7e2f3afde 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessListAdapter.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessListAdapter.java @@ -1,5 +1,6 @@ package com.github.dedis.popstellar.ui.detail.witness; +import android.annotation.SuppressLint; import android.view.*; import android.widget.TextView; @@ -9,12 +10,13 @@ import com.github.dedis.popstellar.R; import com.github.dedis.popstellar.model.objects.security.PublicKey; +import java.util.ArrayList; import java.util.List; /** Adapter to show witnesses of an Event */ public class WitnessListAdapter extends RecyclerView.Adapter { - private List witnesses; + private List witnesses = new ArrayList<>(); public WitnessListAdapter(List witness) { setList(witness); @@ -24,9 +26,12 @@ public void replaceList(List witnesses) { setList(witnesses); } + @SuppressLint("NotifyDataSetChanged") private void setList(List witnesses) { - this.witnesses = witnesses; - notifyDataSetChanged(); + if (witnesses != null) { + this.witnesses = witnesses; + notifyDataSetChanged(); + } } @NonNull diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessMessageListViewAdapter.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessMessageListViewAdapter.java index 07d05ea6c1..bfbb754f9b 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessMessageListViewAdapter.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessMessageListViewAdapter.java @@ -13,7 +13,6 @@ import com.github.dedis.popstellar.model.objects.WitnessMessage; import com.github.dedis.popstellar.ui.detail.LaoDetailViewModel; import com.github.dedis.popstellar.utility.error.ErrorUtils; -import com.github.dedis.popstellar.utility.error.UnknownLaoException; import java.util.List; @@ -80,17 +79,9 @@ public View getView(int position, View convertView, ViewGroup parent) { Context context = parent.getContext(); View.OnClickListener listener = v -> { - boolean isWitness; - try { - isWitness = viewModel.isWitness().getValue(); - } catch (UnknownLaoException e) { - ErrorUtils.logAndShow(context, TAG, R.string.error_no_lao); - return; - } - AlertDialog.Builder adb = new AlertDialog.Builder(context); - if (isWitness) { + if (Boolean.TRUE.equals(viewModel.isWitness().getValue())) { adb.setTitle("Sign Message"); adb.setMessage( " Are you sure you want to sign message with ID : " diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessingFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessingFragment.java index 321a6fb3dd..400c471c18 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessingFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/detail/witness/WitnessingFragment.java @@ -46,6 +46,7 @@ public View onCreateView( public void onResume() { super.onResume(); LaoDetailViewModel viewModel = LaoDetailActivity.obtainViewModel(requireActivity()); - viewModel.setPageTitle(getString(R.string.witnessing)); + viewModel.setPageTitle(R.string.witnessing); + viewModel.setIsTab(true); } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashActivity.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashActivity.java index e4741e716f..cd4441f002 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashActivity.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashActivity.java @@ -12,22 +12,24 @@ import com.github.dedis.popstellar.R; import com.github.dedis.popstellar.databinding.DigitalCashMainActivityBinding; -import com.github.dedis.popstellar.model.objects.security.PublicKey; import com.github.dedis.popstellar.ui.detail.LaoDetailActivity; -import com.github.dedis.popstellar.ui.navigation.NavigationActivity; +import com.github.dedis.popstellar.ui.home.HomeActivity; +import com.github.dedis.popstellar.ui.navigation.LaoActivity; +import com.github.dedis.popstellar.ui.navigation.MainMenuTab; +import com.github.dedis.popstellar.ui.socialmedia.SocialMediaActivity; import com.github.dedis.popstellar.utility.ActivityUtils; import com.github.dedis.popstellar.utility.Constants; import com.github.dedis.popstellar.utility.error.ErrorUtils; -import com.github.dedis.popstellar.utility.error.UnknownLaoException; import java.security.GeneralSecurityException; +import java.util.Objects; import java.util.function.Supplier; import dagger.hilt.android.AndroidEntryPoint; /** Activity for the digital cash */ @AndroidEntryPoint -public class DigitalCashActivity extends NavigationActivity { +public class DigitalCashActivity extends LaoActivity { private DigitalCashViewModel viewModel; private DigitalCashMainActivityBinding binding; public static final String TAG = DigitalCashActivity.class.getSimpleName(); @@ -37,11 +39,18 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DigitalCashMainActivityBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - navigationViewModel = viewModel = obtainViewModel(this); - setupNavigationBar(findViewById(R.id.digital_cash_nav_bar)); - setupTopAppBar(); + laoViewModel = viewModel = obtainViewModel(this); + String laoId = Objects.requireNonNull(getIntent().getStringExtra(Constants.LAO_ID_EXTRA)); + Log.d(TAG, "Opening digitalCash with id " + laoId); + initializeLaoActivity( + laoId, + binding.digitalCashNavigationDrawer, + binding.digitalCashAppBar, + binding.digitalCashDrawerLayout); - loadIntentData(); + laoViewModel.setCurrentTab(MainMenuTab.DIGITAL_CASH); + setupBottomNavBar(); + openHomeTab(); } @Override @@ -57,29 +66,27 @@ public void onStop() { } } - public void loadIntentData() { - if (getIntent().getExtras() != null) { - String id = getIntent().getExtras().getString(Constants.LAO_ID_EXTRA, ""); - viewModel.setLaoId(id); - viewModel.setRollCallId(getIntent().getExtras().getString(Constants.ROLL_CALL_ID, "")); - } - } - - public void openLao() { - startActivity(LaoDetailActivity.newIntentForLao(this, viewModel.getLaoId())); - } - public static DigitalCashViewModel obtainViewModel(FragmentActivity activity) { return new ViewModelProvider(activity).get(DigitalCashViewModel.class); } - @Override - protected DigitalCashTab findTabByMenu(int menuId) { - return DigitalCashTab.findByMenu(menuId); + private void setupBottomNavBar() { + viewModel + .getBottomNavigationTab() + .observe(this, tab -> binding.digitalCashNavBar.setSelectedItemId(tab.getMenuId())); + + binding.digitalCashNavBar.setOnItemSelectedListener( + item -> { + DigitalCashTab tab = DigitalCashTab.findByMenu(item.getItemId()); + Log.i(TAG, "Opening tab : " + tab.getName()); + openBottomTab(tab); + return true; + }); + binding.digitalCashNavBar.setOnItemReselectedListener(item -> {}); + viewModel.setBottomNavigationTab(DigitalCashTab.HOME); } - @Override - protected boolean openTab(DigitalCashTab tab) { + private void openBottomTab(DigitalCashTab tab) { switch (tab) { case HOME: openHomeTab(); @@ -94,16 +101,38 @@ protected boolean openTab(DigitalCashTab tab) { openReceiveTab(); break; case ISSUE: - return openIssueTab(); - default: - Log.w(TAG, "Unhandled tab type : " + tab); + openIssueTab(); } - return true; } @Override - protected DigitalCashTab getDefaultTab() { - return DigitalCashTab.HOME; + protected boolean openTab(MainMenuTab tab) { + Log.d(TAG, "opening drawer tab: " + tab); + switch (tab) { + case INVITE: + openInviteTab(); + break; + case EVENTS: + openEventsTab(); + break; + case SOCIAL_MEDIA: + openSocialMediaTab(); + break; + case DIGITAL_CASH: + break; + case WITNESSING: + openWitnessingTab(); + break; + case TOKENS: + openTokensTab(); + break; + case DISCONNECT: + startActivity(HomeActivity.newIntent(this)); + break; + default: + Log.w(TAG, "Unhandled tab type : " + tab); + } + return false; } private void openHomeTab() { @@ -134,40 +163,39 @@ private void openReceiveTab() { DigitalCashReceiveFragment::newInstance); } - private boolean openIssueTab() { - try { - PublicKey organizerKey = viewModel.getCurrentLao().getOrganizer(); - if (!viewModel.getOwnKey().equals(organizerKey)) { - ErrorUtils.logAndShow(this, TAG, R.string.digital_cash_non_organizer_error_issue); - return false; - } - } catch (UnknownLaoException e) { - ErrorUtils.logAndShow(this, TAG, e, R.string.unknown_lao_exception); - return false; + private void openIssueTab() { + if (!viewModel.isOrganizer()) { + ErrorUtils.logAndShow(this, TAG, R.string.digital_cash_non_organizer_error_issue); } - setCurrentFragment( getSupportFragmentManager(), R.id.fragment_digital_cash_issue, DigitalCashIssueFragment::newInstance); viewModel.setPageTitle(R.string.digital_cash_issue); + } - return true; + private void openSocialMediaTab() { + startActivity(SocialMediaActivity.newIntent(this, viewModel.getLaoId())); } - private void setupTopAppBar() { - viewModel.getPageTitle().observe(this, binding.digitalCashAppBar::setTitle); + private void openInviteTab() { + startActivity( + LaoDetailActivity.newIntentWithTab(this, viewModel.getLaoId(), MainMenuTab.INVITE)); + } - binding.digitalCashAppBar.setNavigationOnClickListener( - v -> { - Fragment fragment = - getSupportFragmentManager().findFragmentById(R.id.fragment_container_digital_cash); - if (fragment instanceof DigitalCashHomeFragment) { - openLao(); - } else { - viewModel.setCurrentTab(DigitalCashTab.HOME); - } - }); + private void openWitnessingTab() { + startActivity( + LaoDetailActivity.newIntentWithTab(this, viewModel.getLaoId(), MainMenuTab.WITNESSING)); + } + + private void openTokensTab() { + startActivity( + LaoDetailActivity.newIntentWithTab(this, viewModel.getLaoId(), MainMenuTab.TOKENS)); + } + + private void openEventsTab() { + startActivity( + LaoDetailActivity.newIntentWithTab(this, viewModel.getLaoId(), MainMenuTab.EVENTS)); } public static Intent newIntent(Context ctx, String laoId) { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashIssueFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashIssueFragment.java index e29fcf51ad..46d1595902 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashIssueFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashIssueFragment.java @@ -141,7 +141,7 @@ private Set attendeesPerRadioGroupButton(int radioGroup, String curre } else if (radioGroup == selectAllRollCallAttendees) { attendees = viewModel.getAttendeesFromLastRollCall(); } else if (radioGroup == selectAllLaoWitnesses) { - attendees = Objects.requireNonNull(viewModel.getCurrentLao()).getWitnesses(); + attendees = Objects.requireNonNull(viewModel.getLao()).getWitnesses(); } return attendees; } @@ -158,7 +158,7 @@ public void setTheAdapterRollCallAttendee() { try { myArray = viewModel.getAttendeesFromTheRollCallList(); } catch (NoRollCallException e) { - viewModel.setCurrentTab(DigitalCashTab.HOME); + viewModel.setBottomNavigationTab(DigitalCashTab.HOME); Log.d(TAG, getString(R.string.error_no_rollcall_closed_in_LAO)); Toast.makeText( requireContext(), diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashSendFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashSendFragment.java index 1d4163e357..02144bb8e6 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashSendFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashSendFragment.java @@ -140,7 +140,7 @@ private void setUpTheAdapter() throws KeyException { try { myArray = viewModel.getAttendeesFromTheRollCallList(); } catch (NoRollCallException e) { - viewModel.setCurrentTab(DigitalCashTab.HOME); + viewModel.setBottomNavigationTab(DigitalCashTab.HOME); Toast.makeText( requireContext(), R.string.digital_cash_please_enter_roll_call, Toast.LENGTH_SHORT) .show(); diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashViewModel.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashViewModel.java index 6922ad6907..af21228b1e 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashViewModel.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/digitalcash/DigitalCashViewModel.java @@ -18,7 +18,7 @@ import com.github.dedis.popstellar.model.objects.view.LaoView; import com.github.dedis.popstellar.repository.*; import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager; -import com.github.dedis.popstellar.ui.navigation.NavigationViewModel; +import com.github.dedis.popstellar.ui.navigation.LaoViewModel; import com.github.dedis.popstellar.utility.ActivityUtils; import com.github.dedis.popstellar.utility.error.UnknownLaoException; import com.github.dedis.popstellar.utility.error.keys.KeyException; @@ -37,10 +37,9 @@ import io.reactivex.Observable; import io.reactivex.*; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.disposables.Disposable; @HiltViewModel -public class DigitalCashViewModel extends NavigationViewModel { +public class DigitalCashViewModel extends LaoViewModel { public static final String TAG = DigitalCashViewModel.class.getSimpleName(); private static final String LAO_FAILURE_MESSAGE = "failed to retrieve lao"; @@ -59,9 +58,6 @@ public class DigitalCashViewModel extends NavigationViewModel { private final MutableLiveData> postTransactionEvent = new MutableLiveData<>(); - private String laoId; - private final MutableLiveData mRollCallId = new MutableLiveData<>(); - /* Is used to change the lao Coin amount on the home fragment*/ private final MutableLiveData> updateLaoCoinEvent = new MutableLiveData<>(); @@ -71,7 +67,9 @@ public class DigitalCashViewModel extends NavigationViewModel { private final MutableLiveData> updateReceiptAmountEvent = new MutableLiveData<>(); - private final MutableLiveData mPageTitle = new MutableLiveData<>(); + + private final MutableLiveData bottomNavigationTab = + new MutableLiveData<>(DigitalCashTab.HOME); /* * Dependencies for this class @@ -111,14 +109,6 @@ protected void onCleared() { disposables.dispose(); } - public LiveData getPageTitle() { - return mPageTitle; - } - - public void setPageTitle(int titleId) { - mPageTitle.postValue(titleId); - } - public LiveData> getPostTransactionEvent() { return postTransactionEvent; } @@ -135,6 +125,16 @@ public LiveData> getUpdateReceiptAddressEvent() { return updateReceiptAddressEvent; } + public LiveData getBottomNavigationTab() { + return bottomNavigationTab; + } + + public void setBottomNavigationTab(DigitalCashTab tab) { + if (tab != bottomNavigationTab.getValue()) { + bottomNavigationTab.setValue(tab); + } + } + public void updateReceiptAddressEvent(String address) { updateReceiptAddressEvent.postValue(new SingleEvent<>(address)); } @@ -201,7 +201,7 @@ public Completable postTransaction( /* Check if a Lao exist */ LaoView laoView; try { - laoView = getCurrentLao(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.e(TAG, LAO_FAILURE_MESSAGE); return Completable.error(new UnknownLaoException()); @@ -272,18 +272,6 @@ private Input processSignInput( new ScriptInput(TYPE, keyPair.getPublicKey(), sig)); } - public String getLaoId() { - return laoId; - } - - public void setLaoId(String laoId) { - this.laoId = laoId; - } - - public void setRollCallId(String rollCallId) { - this.mRollCallId.setValue(rollCallId); - } - public LAORepository getLaoRepository() { return laoRepository; } @@ -294,12 +282,12 @@ public PublicKey getOwnKey() { @Nullable public Set getAttendeesFromLastRollCall() throws NoRollCallException { - return rollCallRepo.getLastClosedRollCall(laoId).getAttendees(); + return rollCallRepo.getLastClosedRollCall(getLaoId()).getAttendees(); } @Nullable public PublicKey getOrganizer() throws UnknownLaoException { - return getCurrentLao().getOrganizer(); + return getLao().getOrganizer(); } @Nullable @@ -309,16 +297,17 @@ public List getAttendeesFromTheRollCallList() throws NoRollCallException .collect(Collectors.toList()); } - public LaoView getCurrentLao() throws UnknownLaoException { - return laoRepository.getLaoView(laoId); + @Override + public LaoView getLao() throws UnknownLaoException { + return laoRepository.getLaoView(getLaoId()); } public Set getAllAttendees() { - return rollCallRepo.getAllAttendeesInLao(laoId); + return rollCallRepo.getAllAttendeesInLao(getLaoId()); } public PoPToken getValidToken() throws KeyException { - return keyManager.getValidPoPToken(laoId, rollCallRepo.getLastClosedRollCall(laoId)); + return keyManager.getValidPoPToken(getLaoId(), rollCallRepo.getLastClosedRollCall(getLaoId())); } public boolean canPerformTransaction( @@ -365,39 +354,26 @@ private void processNotCoinbaseTransaction( } public List getTransactionsForUser(PublicKey user) { - return digitalCashRepo.getTransactions(laoId, user); + return digitalCashRepo.getTransactions(getLaoId(), user); } public Observable> getTransactionsObservable() { try { - return digitalCashRepo.getTransactionsObservable(laoId, getValidToken().getPublicKey()); + return digitalCashRepo.getTransactionsObservable(getLaoId(), getValidToken().getPublicKey()); } catch (KeyException e) { return Observable.error(e); } } public Observable> getRollCallsObservable() { - return rollCallRepo.getRollCallsObservableInLao(laoId); + return rollCallRepo.getRollCallsObservableInLao(getLaoId()); } public long getUserBalance(PublicKey user) { - return digitalCashRepo.getUserBalance(laoId, user); + return digitalCashRepo.getUserBalance(getLaoId(), user); } public long getOwnBalance() throws KeyException { return getUserBalance(getValidToken().getPublicKey()); } - - /** - * This function should be used to add disposable object generated from subscription to sent - * messages flows - * - *

They will be disposed of when the view model is cleaned which ensures that the subscription - * stays relevant throughout the whole lifecycle of the activity and it is not bound to a fragment - * - * @param disposable to add - */ - public void addDisposable(Disposable disposable) { - this.disposables.add(disposable); - } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/LaoActivity.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/LaoActivity.java new file mode 100644 index 0000000000..2548fe4080 --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/LaoActivity.java @@ -0,0 +1,227 @@ +package com.github.dedis.popstellar.ui.navigation; + +import android.util.Log; +import android.widget.TextView; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.GravityCompat; +import androidx.drawerlayout.widget.DrawerLayout; + +import com.github.dedis.popstellar.R; +import com.github.dedis.popstellar.model.Role; +import com.github.dedis.popstellar.model.objects.RollCall; +import com.github.dedis.popstellar.model.objects.Wallet; +import com.github.dedis.popstellar.model.objects.security.PublicKey; +import com.github.dedis.popstellar.repository.LAORepository; +import com.github.dedis.popstellar.repository.RollCallRepository; +import com.github.dedis.popstellar.ui.home.HomeActivity; +import com.github.dedis.popstellar.utility.error.ErrorUtils; +import com.github.dedis.popstellar.utility.error.UnknownLaoException; +import com.github.dedis.popstellar.utility.error.keys.*; +import com.github.dedis.popstellar.utility.security.KeyManager; +import com.google.android.material.appbar.MaterialToolbar; +import com.google.android.material.navigation.NavigationView; + +import java.util.ArrayList; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; +import io.reactivex.android.schedulers.AndroidSchedulers; + +/** + * This abstract activity encapsulate the redundant behavior of an activity in a LAO + * + *

An activity extending this must instantiate the laoViewModel in its onCreate and it should + * call initializeLaoActivity + */ +@AndroidEntryPoint +public abstract class LaoActivity extends AppCompatActivity { + + private static final String TAG = LaoActivity.class.getSimpleName(); + + protected LaoViewModel laoViewModel; + + @Inject RollCallRepository rollCallRepo; + @Inject LAORepository laoRepo; + @Inject KeyManager keyManager; + @Inject Wallet wallet; + + /** + * Initialize the parent activity. It sets up views common to the 3 activities + * + * @param laoId the id of the LAO + * @param navigationView the drawer navigation view + * @param toolbar the activity toolbar + * @param drawerLayout the drawer layout + */ + protected void initializeLaoActivity( + String laoId, + NavigationView navigationView, + MaterialToolbar toolbar, + DrawerLayout drawerLayout) { + + laoViewModel.setLaoId(laoId); + observeRoles(); + + // Listen to click on left icon of toolbar + toolbar.setNavigationOnClickListener( + view -> { + if (Boolean.TRUE.equals(laoViewModel.isTab().getValue())) { + // If it is a tab open menu + drawerLayout.openDrawer(GravityCompat.START); + } else { + // Press back arrow + onBackPressed(); + } + }); + + // Observe whether the menu icon or back arrow should be displayed + laoViewModel + .isTab() + .observe( + this, + isTab -> + toolbar.setNavigationIcon( + Boolean.TRUE.equals(isTab) + ? R.drawable.menu_icon + : R.drawable.back_arrow_icon)); + + // Observe changes to the tab selected + laoViewModel + .getCurrentTab() + .observe( + this, + tab -> { + laoViewModel.setIsTab(true); + navigationView.setCheckedItem(tab.getMenuId()); + }); + + navigationView.setNavigationItemSelectedListener( + item -> { + MainMenuTab tab = MainMenuTab.findByMenu(item.getItemId()); + Log.i(TAG, "Opening tab : " + tab.getName()); + boolean selected = openTab(tab); + if (selected) { + Log.d(TAG, "The tab was successfully opened"); + laoViewModel.setCurrentTab(tab); + } else { + Log.d(TAG, "The tab wasn't opened"); + } + drawerLayout.close(); + return selected; + }); + + observeLao(laoId); + try { + setupHeaderLaoName(navigationView, laoViewModel.getLao().getName()); + } catch (UnknownLaoException e) { + ErrorUtils.logAndShow(this, TAG, e, R.string.unknown_lao_exception); + startActivity(HomeActivity.newIntent(this)); + } + + // Update the user's role in the drawer header when it changes + laoViewModel.getRole().observe(this, role -> setupHeaderRole(navigationView, role)); + + // Observe the toolbar title to display + laoViewModel + .getPageTitle() + .observe( + this, + resId -> { + if (resId != 0) { + toolbar.setTitle(resId); + } + }); + + observeRollCalls(laoId); + } + + private void observeRoles() { + // Observe any change in the following variable to update the role + laoViewModel.isWitness().observe(this, any -> laoViewModel.updateRole()); + laoViewModel.isAttendee().observe(this, any -> laoViewModel.updateRole()); + } + + private void observeRollCalls(String laoId) { + laoViewModel.addDisposable( + rollCallRepo + .getRollCallsObservableInLao(laoId) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + rollCalls -> { + boolean isLastRollCallAttended = + rollCalls.stream() + .filter(rc -> isRollCallAttended(rc, laoId)) + .anyMatch( + rc -> { + try { + return rc.equals(rollCallRepo.getLastClosedRollCall(laoId)); + } catch (NoRollCallException e) { + return false; + } + }); + laoViewModel.setIsAttendee(isLastRollCallAttended); + }, + error -> + ErrorUtils.logAndShow(this, TAG, error, R.string.unknown_roll_call_exception))); + } + + private void observeLao(String laoId) { + laoViewModel.addDisposable( + laoRepo + .getLaoObservable(laoId) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + laoView -> { + Log.d(TAG, "got an update for lao: " + laoView); + + laoViewModel.setWitnessMessages( + new ArrayList<>(laoView.getWitnessMessages().values())); + laoViewModel.setWitnesses(new ArrayList<>(laoView.getWitnesses())); + + boolean isOrganizer = + laoView.getOrganizer().equals(keyManager.getMainPublicKey()); + laoViewModel.setIsOrganizer(isOrganizer); + laoViewModel.setIsWitness( + laoView.getWitnesses().contains(keyManager.getMainPublicKey())); + + laoViewModel.updateRole(); + }, + error -> Log.d(TAG, "error updating LAO :" + error))); + } + + private void setupHeaderLaoName(NavigationView navigationView, String laoName) { + TextView laoNameView = + navigationView + .getHeaderView(0) // We have only one header + .findViewById(R.id.drawer_header_lao_title); + laoNameView.setText(laoName); + } + + private void setupHeaderRole(NavigationView navigationView, Role role) { + TextView roleView = + navigationView + .getHeaderView(0) // We have only one header + .findViewById(R.id.drawer_header_role); + roleView.setText(role.getStringId()); + } + + private boolean isRollCallAttended(RollCall rollcall, String laoId) { + try { + PublicKey pk = wallet.generatePoPToken(laoId, rollcall.getPersistentId()).getPublicKey(); + return rollcall.isClosed() && rollcall.getAttendees().contains(pk); + } catch (KeyGenerationException | UninitializedWalletException e) { + Log.e(TAG, "failed to retrieve public key from wallet", e); + return false; + } + } + + /** + * Open the provided tab + * + * @param tab to pen + * @return true if the tab was actually opened and the menu should be selected + */ + protected abstract boolean openTab(MainMenuTab tab); +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/LaoViewModel.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/LaoViewModel.java new file mode 100644 index 0000000000..bea392dced --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/LaoViewModel.java @@ -0,0 +1,169 @@ +package com.github.dedis.popstellar.ui.navigation; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.lifecycle.*; + +import com.github.dedis.popstellar.model.Role; +import com.github.dedis.popstellar.model.objects.WitnessMessage; +import com.github.dedis.popstellar.model.objects.security.PublicKey; +import com.github.dedis.popstellar.model.objects.view.LaoView; +import com.github.dedis.popstellar.utility.error.UnknownLaoException; + +import java.util.ArrayList; +import java.util.List; + +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.disposables.Disposable; + +/** Abstract view model that provides the common interface for lao activities */ +public abstract class LaoViewModel extends AndroidViewModel { + + private final MutableLiveData currentTab = new MutableLiveData<>(); + + private boolean isOrganizer = false; + private String laoId; + + private final MutableLiveData isWitness = new MutableLiveData<>(Boolean.FALSE); + private final MutableLiveData isAttendee = new MutableLiveData<>(Boolean.FALSE); + private final MutableLiveData role = new MutableLiveData<>(Role.MEMBER); + private final MutableLiveData isTab = new MutableLiveData<>(Boolean.TRUE); + private final MutableLiveData pageTitle = new MutableLiveData<>(0); + private final MutableLiveData> witnesses = + new MutableLiveData<>(new ArrayList<>()); + private final MutableLiveData> witnessMessages = + new MutableLiveData<>(new ArrayList<>()); + + private final CompositeDisposable disposables = new CompositeDisposable(); + + protected LaoViewModel(@NonNull Application application) { + super(application); + } + + public String getLaoId() { + return laoId; + } + + public LiveData getCurrentTab() { + return currentTab; + } + + public void setCurrentTab(MainMenuTab tab) { + currentTab.setValue(tab); + } + + public boolean isOrganizer() { + return isOrganizer; + } + + public MutableLiveData isWitness() { + return isWitness; + } + + public MutableLiveData isAttendee() { + return isAttendee; + } + + public MutableLiveData getRole() { + return role; + } + + public MutableLiveData isTab() { + return isTab; + } + + public MutableLiveData getPageTitle() { + return pageTitle; + } + + public abstract LaoView getLao() throws UnknownLaoException; + + public LiveData> getWitnesses() { + return witnesses; + } + + public LiveData> getWitnessMessages() { + return witnessMessages; + } + + public void updateRole() { + Role currentRole = determineRole(); + if (role.getValue() != currentRole) { + this.role.setValue(currentRole); + } + } + + public void setLaoId(String laoId) { + this.laoId = laoId; + } + + public void setIsOrganizer(boolean isOrganizer) { + this.isOrganizer = isOrganizer; + } + + public void setIsWitness(boolean isWitness) { + if (!Boolean.valueOf(isWitness).equals(this.isWitness.getValue())) { + this.isWitness.setValue(isWitness); + } + } + + public void setIsAttendee(boolean isAttendee) { + if (!Boolean.valueOf(isAttendee).equals(this.isAttendee.getValue())) { + this.isAttendee.setValue(isAttendee); + } + } + + public void setIsTab(boolean isTab) { + if (!Boolean.valueOf(isTab).equals(this.isTab.getValue())) { + this.isTab.setValue(isTab); + } + } + + public void setPageTitle(@StringRes Integer pageTitle) { + if (!this.pageTitle.getValue().equals(pageTitle)) { + this.pageTitle.setValue(pageTitle); + } + } + + public void setWitnesses(List witnesses) { + this.witnesses.setValue(witnesses); + } + + public void setWitnessMessages(List messages) { + this.witnessMessages.setValue(messages); + } + + /** + * This function should be used to add disposable object generated from subscription to sent + * messages flows + * + *

They will be disposed of when the view model is cleaned which ensures that the subscription + * stays relevant throughout the whole lifecycle of the activity and it is not bound to a fragment + * + * @param disposable to add + */ + public void addDisposable(Disposable disposable) { + this.disposables.add(disposable); + } + + @Override + protected void onCleared() { + super.onCleared(); + disposables.dispose(); + } + + private Role determineRole() { + if (isOrganizer) { + return Role.ORGANIZER; + } + if (Boolean.TRUE.equals(isWitness.getValue())) { + return Role.WITNESS; + } + if (Boolean.TRUE.equals(isAttendee.getValue())) { + return Role.ATTENDEE; + } + return Role.MEMBER; + } +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/MainMenuTab.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/MainMenuTab.java new file mode 100644 index 0000000000..78726ae1a4 --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/MainMenuTab.java @@ -0,0 +1,45 @@ +package com.github.dedis.popstellar.ui.navigation; + +import androidx.annotation.IdRes; + +import com.github.dedis.popstellar.R; + +import java.util.Arrays; +import java.util.List; + +public enum MainMenuTab { + INVITE(R.id.main_menu_invite), + EVENTS(R.id.main_menu_event_list), + SOCIAL_MEDIA(R.id.main_menu_social_media), + DIGITAL_CASH(R.id.main_menu_digital_cash), + WITNESSING(R.id.main_menu_witnessing), + TOKENS(R.id.main_menu_tokens), + DISCONNECT(R.id.main_menu_disconnect); + + private static final List ALL = Arrays.asList(values()); + + public static MainMenuTab findByMenu(@IdRes int menuId) { + for (MainMenuTab tab : ALL) { + if (tab.menuId == menuId) { + return tab; + } + } + + throw new IllegalArgumentException("Unknown id : " + menuId); + } + + @IdRes private final int menuId; + + MainMenuTab(@IdRes int menuId) { + this.menuId = menuId; + } + + @IdRes + public int getMenuId() { + return menuId; + } + + public String getName() { + return toString(); + } +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/NavigationActivity.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/NavigationActivity.java deleted file mode 100644 index c068adef06..0000000000 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/NavigationActivity.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.dedis.popstellar.ui.navigation; - -import android.util.Log; - -import androidx.appcompat.app.AppCompatActivity; - -import com.google.android.material.navigation.NavigationBarView; - -/** - * This abstract activity encapsulate the redundant behavior of an activity with a navigation bar - * - *

An activity extending this must instantiate the navigationViewModel in its onCreate and it - * should call setupNavigationBar with the navigationView as parameter. - */ -public abstract class NavigationActivity extends AppCompatActivity { - - private static final String TAG = NavigationActivity.class.getSimpleName(); - - protected NavigationViewModel navigationViewModel; - - /** - * Setup the navigation bar listeners given the navigation bar view - * - *

This function should be called in the activity's onCreate after the navigation view model - * has been set - * - * @param navbar the view - */ - protected void setupNavigationBar(NavigationBarView navbar) { - navigationViewModel - .getCurrentTab() - .observe(this, tab -> navbar.setSelectedItemId(tab.getMenuId())); - navbar.setOnItemSelectedListener( - item -> { - T tab = findTabByMenu(item.getItemId()); - Log.i(TAG, "Opening tab : " + tab.getName()); - boolean selected = openTab(tab); - if (selected) { - Log.d(TAG, "The tab was successfully opened"); - navigationViewModel.setCurrentTab(tab); - } else { - Log.d(TAG, "The tab wasn't opened"); - } - return selected; - }); - // Set an empty reselect listener to disable the onSelectListener when pressing multiple times - navbar.setOnItemReselectedListener(item -> {}); - - openDefaultTab(navbar); - } - - private void openDefaultTab(NavigationBarView navbar) { - T defaultTab = getDefaultTab(); - navigationViewModel.setCurrentTab(defaultTab); - - // If the tab is already selected, the event will not be dispatched and we need to do it - // manually - if (defaultTab.getMenuId() == navbar.getSelectedItemId()) { - openTab(defaultTab); - } - } - - /** - * Returns a Tab instance given its menu id - * - * @param menuId of the tab - * @return the tab - */ - protected abstract T findTabByMenu(int menuId); - - /** - * Open the provided tab - * - * @param tab to pen - * @return true if the tab was actually opened and the menu should be selected - */ - protected abstract boolean openTab(T tab); - - /** - * @return the tab to open by default - */ - protected abstract T getDefaultTab(); -} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/NavigationViewModel.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/NavigationViewModel.java deleted file mode 100644 index eb61d21422..0000000000 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/navigation/NavigationViewModel.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.dedis.popstellar.ui.navigation; - -import android.app.Application; - -import androidx.annotation.NonNull; -import androidx.lifecycle.*; - -/** - * Abstract view model that provides the components of the navigation bar - * - *

Any activity that uses a navigation bar should have its view model extends from the this * - * class - * - * @param the type fo the Tab used - */ -public abstract class NavigationViewModel extends AndroidViewModel { - - private final MutableLiveData currentTab = new MutableLiveData<>(); - - protected NavigationViewModel(@NonNull Application application) { - super(application); - } - - public LiveData getCurrentTab() { - return currentTab; - } - - public void setCurrentTab(T tab) { - currentTab.postValue(tab); - } -} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/qrcode/QRCodeScanningFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/qrcode/QRCodeScanningFragment.java index b10a80f3bc..52a5134b40 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/qrcode/QRCodeScanningFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/qrcode/QRCodeScanningFragment.java @@ -103,7 +103,7 @@ public View onCreateView( binding.addAttendeeConfirm.setVisibility(View.VISIBLE); LaoDetailViewModel laoDetailViewModel = (LaoDetailViewModel) viewModel; - laoDetailViewModel.setPageTitle(getString(R.string.add_attendee_title)); + laoDetailViewModel.setPageTitle(R.string.add_attendee_title); // Subscribe to " Nb of attendees" event observeNbAttendeesEvent(); @@ -117,7 +117,7 @@ public View onCreateView( if (viewModel.getScanningAction() == ScanningAction.ADD_WITNESS) { LaoDetailViewModel laoDetailViewModel = (LaoDetailViewModel) viewModel; - laoDetailViewModel.setPageTitle(getString(R.string.add_witness_description)); + laoDetailViewModel.setPageTitle(R.string.add_witness_description); // Subscribe to " Witness scan confirm " event observeWitnessScanConfirmEvent(); diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaActivity.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaActivity.java index d44313671d..89c99971e1 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaActivity.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaActivity.java @@ -13,24 +13,27 @@ import com.github.dedis.popstellar.R; import com.github.dedis.popstellar.databinding.SocialMediaActivityBinding; import com.github.dedis.popstellar.ui.detail.LaoDetailActivity; -import com.github.dedis.popstellar.ui.navigation.NavigationActivity; +import com.github.dedis.popstellar.ui.digitalcash.DigitalCashActivity; +import com.github.dedis.popstellar.ui.home.HomeActivity; +import com.github.dedis.popstellar.ui.navigation.LaoActivity; +import com.github.dedis.popstellar.ui.navigation.MainMenuTab; import com.github.dedis.popstellar.utility.ActivityUtils; +import com.github.dedis.popstellar.utility.Constants; import java.security.GeneralSecurityException; +import java.util.Objects; import java.util.function.Supplier; import dagger.hilt.android.AndroidEntryPoint; /** Activity for the social media */ @AndroidEntryPoint -public class SocialMediaActivity extends NavigationActivity { +public class SocialMediaActivity extends LaoActivity { private SocialMediaViewModel viewModel; private SocialMediaActivityBinding binding; public static final String TAG = SocialMediaActivity.class.getSimpleName(); - public static final String LAO_ID = "LAO_ID"; - public static final String LAO_NAME = "LAO_NAME"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -38,23 +41,18 @@ protected void onCreate(Bundle savedInstanceState) { binding = SocialMediaActivityBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - navigationViewModel = viewModel = obtainViewModel(this); - - // When we launch the social media from a lao, it directly sets its id and name - if (getIntent().getExtras() != null) { - String laoId = getIntent().getExtras().getString(LAO_ID); - String laoName = getIntent().getExtras().getString(LAO_NAME); - - if (laoId != null) { - viewModel.setLaoId(laoId); - } - if (laoName != null) { - viewModel.setLaoName(laoName); - } - } - - setupNavigationBar(binding.socialMediaNavBar); - setupTopAppBar(); + laoViewModel = viewModel = obtainViewModel(this); + + String laoId = Objects.requireNonNull(getIntent().getStringExtra(Constants.LAO_ID_EXTRA)); + + laoViewModel.setCurrentTab(MainMenuTab.SOCIAL_MEDIA); + initializeLaoActivity( + laoId, + binding.socialMediaNavigationDrawer, + binding.socialMediaAppBar, + binding.socialMediaDrawerLayout); + setupBottomNavBar(); + openHomeTab(); } @Override @@ -74,13 +72,24 @@ public static SocialMediaViewModel obtainViewModel(FragmentActivity activity) { return new ViewModelProvider(activity).get(SocialMediaViewModel.class); } - @Override - protected SocialMediaTab findTabByMenu(int menuId) { - return SocialMediaTab.findByMenu(menuId); + private void setupBottomNavBar() { + viewModel + .getBottomNavigationTab() + .observe(this, tab -> binding.socialMediaNavBar.setSelectedItemId(tab.getMenuId())); + + binding.socialMediaNavBar.setOnItemSelectedListener( + item -> { + SocialMediaTab tab = SocialMediaTab.findByMenu(item.getItemId()); + Log.i(TAG, "Opening tab : " + tab.getName()); + openBottomTab(tab); + return true; + }); + + binding.socialMediaNavBar.setOnItemReselectedListener(item -> {}); + viewModel.setBottomNavigationTab(SocialMediaTab.HOME); } - @Override - protected boolean openTab(SocialMediaTab tab) { + private void openBottomTab(SocialMediaTab tab) { switch (tab) { case HOME: openHomeTab(); @@ -94,15 +103,36 @@ protected boolean openTab(SocialMediaTab tab) { case PROFILE: openProfileTab(); break; - default: - Log.w(TAG, "Unhandled tab type : " + tab); } - return true; } @Override - protected SocialMediaTab getDefaultTab() { - return SocialMediaTab.HOME; + protected boolean openTab(MainMenuTab tab) { + switch (tab) { + case INVITE: + openInviteTab(); + break; + case EVENTS: + openEventsTab(); + break; + case SOCIAL_MEDIA: + break; + case DIGITAL_CASH: + openDigitalCash(); + break; + case WITNESSING: + openWitnessingTab(); + break; + case TOKENS: + openTokensTab(); + break; + case DISCONNECT: + startActivity(HomeActivity.newIntent(this)); + break; + default: + Log.w(TAG, "Unhandled tab type : " + tab); + } + return false; } private void openHomeTab() { @@ -126,10 +156,6 @@ private void openFollowingTab() { SocialMediaFollowingFragment::newInstance); } - public void openLao() { - startActivity(LaoDetailActivity.newIntentForLao(this, viewModel.getLaoId())); - } - private void openProfileTab() { setCurrentFragment( getSupportFragmentManager(), @@ -137,31 +163,33 @@ private void openProfileTab() { SocialMediaProfileFragment::newInstance); } - private void setupTopAppBar() { - viewModel.getPageTitle().observe(this, binding.socialMediaAppBar::setTitle); - - binding.socialMediaAppBar.setNavigationOnClickListener( - v -> { - Fragment fragment = - getSupportFragmentManager().findFragmentById(R.id.fragment_container_social_media); - if (fragment instanceof SocialMediaHomeFragment) { - openLao(); - } else { - if (viewModel.getCurrentTab().getValue() == SocialMediaTab.HOME) { - // On reselection the navigation is supposed to do nothing to prevent loops, so we - // manually change the fragment - openHomeTab(); - } else { - viewModel.setCurrentTab(SocialMediaTab.HOME); - } - } - }); + private void openInviteTab() { + startActivity( + LaoDetailActivity.newIntentWithTab(this, viewModel.getLaoId(), MainMenuTab.INVITE)); + } + + private void openWitnessingTab() { + startActivity( + LaoDetailActivity.newIntentWithTab(this, viewModel.getLaoId(), MainMenuTab.WITNESSING)); + } + + private void openTokensTab() { + startActivity( + LaoDetailActivity.newIntentWithTab(this, viewModel.getLaoId(), MainMenuTab.TOKENS)); + } + + private void openEventsTab() { + startActivity( + LaoDetailActivity.newIntentWithTab(this, viewModel.getLaoId(), MainMenuTab.EVENTS)); + } + + private void openDigitalCash() { + startActivity(DigitalCashActivity.newIntent(this, viewModel.getLaoId())); } - public static Intent newIntent(Context ctx, String laoId, String laoName) { + public static Intent newIntent(Context ctx, String laoId) { Intent intent = new Intent(ctx, SocialMediaActivity.class); - intent.putExtra(LAO_ID, laoId); - intent.putExtra(LAO_NAME, laoName); + intent.putExtra(Constants.LAO_ID_EXTRA, laoId); return intent; } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaSendFragment.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaSendFragment.java index f45d4a0335..16c199bf50 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaSendFragment.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaSendFragment.java @@ -93,7 +93,7 @@ private void sendNewChirp() { requireContext(), TAG, error, R.string.error_sending_chirp); } })); - viewModel.setCurrentTab(SocialMediaTab.HOME); + viewModel.setBottomNavigationTab(SocialMediaTab.HOME); } } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaViewModel.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaViewModel.java index cd0bf6f676..c4ca425463 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaViewModel.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/socialmedia/SocialMediaViewModel.java @@ -18,7 +18,7 @@ import com.github.dedis.popstellar.model.objects.view.LaoView; import com.github.dedis.popstellar.repository.*; import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager; -import com.github.dedis.popstellar.ui.navigation.NavigationViewModel; +import com.github.dedis.popstellar.ui.navigation.LaoViewModel; import com.github.dedis.popstellar.utility.ActivityUtils; import com.github.dedis.popstellar.utility.error.ErrorUtils; import com.github.dedis.popstellar.utility.error.UnknownLaoException; @@ -37,22 +37,20 @@ import io.reactivex.Observable; import io.reactivex.Single; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.disposables.Disposable; @HiltViewModel -public class SocialMediaViewModel extends NavigationViewModel { +public class SocialMediaViewModel extends LaoViewModel { public static final String TAG = SocialMediaViewModel.class.getSimpleName(); private static final String LAO_FAILURE_MESSAGE = "failed to retrieve lao"; private static final String SOCIAL = "social"; public static final Integer MAX_CHAR_NUMBERS = 300; - private String laoId; /* * LiveData objects for capturing events */ private final MutableLiveData mNumberCharsLeft = new MutableLiveData<>(); - private final MutableLiveData mLaoName = new MutableLiveData<>(); - private final MutableLiveData mPageTitle = new MutableLiveData<>(); + private final MutableLiveData bottomNavigationTab = + new MutableLiveData<>(SocialMediaTab.HOME); /* * Dependencies for this class @@ -104,14 +102,6 @@ public LiveData getNumberCharsLeft() { return mNumberCharsLeft; } - public LiveData getLaoName() { - return mLaoName; - } - - public LiveData getPageTitle() { - return mPageTitle; - } - /* * Methods that modify the state or post an Event to update the UI. */ @@ -120,18 +110,15 @@ public void setNumberCharsLeft(Integer numberChars) { mNumberCharsLeft.setValue(numberChars); } - public void setLaoId(String laoId) { - this.laoId = laoId; - } - - public void setLaoName(String laoName) { - mLaoName.setValue(laoName); + public LiveData getBottomNavigationTab() { + return bottomNavigationTab; } - public void setPageTitle(int titleId) { - mPageTitle.postValue(titleId); + public void setBottomNavigationTab(SocialMediaTab tab) { + if (tab != bottomNavigationTab.getValue()) { + bottomNavigationTab.setValue(tab); + } } - /** * Send a chirp to your own channel. * @@ -147,7 +134,7 @@ public Single sendChirp( LaoView laoView; try { - laoView = getCurrentLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.e(TAG, LAO_FAILURE_MESSAGE); return Single.error(new UnknownLaoException()); @@ -175,7 +162,7 @@ public Single deleteChirp(MessageID chirpId, long timestamp) { final LaoView laoView; try { - laoView = getCurrentLaoView(); + laoView = getLao(); } catch (UnknownLaoException e) { Log.e(TAG, LAO_FAILURE_MESSAGE); return Single.error(new UnknownLaoException()); @@ -200,13 +187,13 @@ public Single deleteChirp(MessageID chirpId, long timestamp) { public Observable> getChirps() { return socialMediaRepository - .getChirpsOfLao(laoId) + .getChirpsOfLao(getLaoId()) // Retrieve chirp subjects per id .map( ids -> { List> chirps = new ArrayList<>(ids.size()); for (MessageID id : ids) { - chirps.add(socialMediaRepository.getChirp(laoId, id)); + chirps.add(socialMediaRepository.getChirp(getLaoId(), id)); } return chirps; }) @@ -250,32 +237,12 @@ public void savePersistentData() throws GeneralSecurityException { networkManager, wallet, getApplication().getApplicationContext()); } - /** - * This function should be used to add disposable object generated from subscription to sent - * messages flows - * - *

They will be disposed of when the view model is cleaned which ensures that the subscription - * stays relevant throughout the whole lifecycle of the activity and it is not bound to a fragment - * - * @param disposable to add - */ - public void addDisposable(Disposable disposable) { - this.disposables.add(disposable); - } - - public LaoView getLaoView(String laoId) throws UnknownLaoException { - return laoRepository.getLaoView(laoId); - } - - public LaoView getCurrentLaoView() throws UnknownLaoException { - return getLaoView(laoId); - } - - public String getLaoId() { - return laoId; + @Override + public LaoView getLao() throws UnknownLaoException { + return laoRepository.getLaoView(getLaoId()); } public PoPToken getValidPoPToken() throws KeyException { - return keyManager.getValidPoPToken(laoId, rollCallRepo.getLastClosedRollCall(laoId)); + return keyManager.getValidPoPToken(getLaoId(), rollCallRepo.getLastClosedRollCall(getLaoId())); } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/Constants.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/Constants.java index 0dab638d54..e3c452fa62 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/Constants.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/Constants.java @@ -23,6 +23,9 @@ public class Constants { /** The extra value given to the LaoDetailActivity when the wallet content is to be opened */ public static final String CONTENT_WALLET_EXTRA = "content_wallet"; + /** The tab to open in LaoDetailActivity */ + public static final String TAB_EXTRA = "tab_extra"; + /** The extra value given to the RollCallFragment when opened */ public static final String RC_PK_EXTRA = "pk"; diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/error/ErrorUtils.java b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/error/ErrorUtils.java index aeb69bf03f..7e521fa7ce 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/error/ErrorUtils.java +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/error/ErrorUtils.java @@ -29,7 +29,7 @@ private ErrorUtils() throws IllegalAccessException { public static void logAndShow(Context context, String tag, @StringRes int action) { String message = context.getString(action); - Log.d(tag, message); + Log.e(tag, message); displayToast(context, message); } diff --git a/fe2-android/app/src/main/res/drawable/back_arrow_icon.xml b/fe2-android/app/src/main/res/drawable/back_arrow_icon.xml new file mode 100644 index 0000000000..ab7e1d6373 --- /dev/null +++ b/fe2-android/app/src/main/res/drawable/back_arrow_icon.xml @@ -0,0 +1,11 @@ + + + diff --git a/fe2-android/app/src/main/res/drawable/disconnect_icon.xml b/fe2-android/app/src/main/res/drawable/disconnect_icon.xml new file mode 100644 index 0000000000..2ef68a2f3c --- /dev/null +++ b/fe2-android/app/src/main/res/drawable/disconnect_icon.xml @@ -0,0 +1,12 @@ + + + + diff --git a/fe2-android/app/src/main/res/drawable/invite_icon.xml b/fe2-android/app/src/main/res/drawable/invite_icon.xml new file mode 100644 index 0000000000..237863e54f --- /dev/null +++ b/fe2-android/app/src/main/res/drawable/invite_icon.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/fe2-android/app/src/main/res/drawable/menu_icon.xml b/fe2-android/app/src/main/res/drawable/menu_icon.xml new file mode 100644 index 0000000000..64fecd8fde --- /dev/null +++ b/fe2-android/app/src/main/res/drawable/menu_icon.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/fe2-android/app/src/main/res/layout/digital_cash_main_activity.xml b/fe2-android/app/src/main/res/layout/digital_cash_main_activity.xml index 397b61664c..05c8e2b358 100644 --- a/fe2-android/app/src/main/res/layout/digital_cash_main_activity.xml +++ b/fe2-android/app/src/main/res/layout/digital_cash_main_activity.xml @@ -1,43 +1,65 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> - + android:layout_height="match_parent" + tools:openDrawer="start"> - + android:layout_height="match_parent" + android:fitsSystemWindows="true"> - - + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - + + + + + + + + + + + + + - + diff --git a/fe2-android/app/src/main/res/layout/header_navigation_drawer.xml b/fe2-android/app/src/main/res/layout/header_navigation_drawer.xml new file mode 100644 index 0000000000..78dc351fc6 --- /dev/null +++ b/fe2-android/app/src/main/res/layout/header_navigation_drawer.xml @@ -0,0 +1,37 @@ + + + + + + + + + diff --git a/fe2-android/app/src/main/res/layout/identity_fragment.xml b/fe2-android/app/src/main/res/layout/identity_fragment.xml deleted file mode 100644 index 349f3fc728..0000000000 --- a/fe2-android/app/src/main/res/layout/identity_fragment.xml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fe2-android/app/src/main/res/layout/invite_fragment.xml b/fe2-android/app/src/main/res/layout/invite_fragment.xml new file mode 100644 index 0000000000..405d235d37 --- /dev/null +++ b/fe2-android/app/src/main/res/layout/invite_fragment.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fe2-android/app/src/main/res/layout/lao_detail_activity.xml b/fe2-android/app/src/main/res/layout/lao_detail_activity.xml index 18bff7db4f..4c8aed6f92 100644 --- a/fe2-android/app/src/main/res/layout/lao_detail_activity.xml +++ b/fe2-android/app/src/main/res/layout/lao_detail_activity.xml @@ -1,170 +1,53 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> - + android:layout_height="match_parent" + tools:openDrawer="start"> - + android:layout_height="match_parent" + android:fitsSystemWindows="true"> - - + android:layout_height="wrap_content" + style="@style/Widget.MaterialComponents.AppBarLayout.Primary" + android:fitsSystemWindows="true"> - - - - - + + - - - - - - - - - - - - - - - - - - - + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> - + - + - + - - - - diff --git a/fe2-android/app/src/main/res/layout/lao_detail_fragment.xml b/fe2-android/app/src/main/res/layout/lao_detail_fragment.xml index f5ee95b385..3fc988f2b5 100644 --- a/fe2-android/app/src/main/res/layout/lao_detail_fragment.xml +++ b/fe2-android/app/src/main/res/layout/lao_detail_fragment.xml @@ -2,15 +2,6 @@ - - - - - - - - + android:layout_height="match_parent" + tools:openDrawer="start"> - + android:layout_height="match_parent" + android:fitsSystemWindows="true"> - - + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - + + + + + + android:layout_height="wrap_content" + android:fitsSystemWindows="true"> - + + - - + + + + + diff --git a/fe2-android/app/src/main/res/menu/lao_detail_tool_menu.xml b/fe2-android/app/src/main/res/menu/lao_detail_tool_menu.xml deleted file mode 100644 index a058098ddf..0000000000 --- a/fe2-android/app/src/main/res/menu/lao_detail_tool_menu.xml +++ /dev/null @@ -1,11 +0,0 @@ - -

- - diff --git a/fe2-android/app/src/main/res/menu/lao_detail_menu.xml b/fe2-android/app/src/main/res/menu/main_menu.xml similarity index 55% rename from fe2-android/app/src/main/res/menu/lao_detail_menu.xml rename to fe2-android/app/src/main/res/menu/main_menu.xml index bc68ced262..0bef3cd1c0 100644 --- a/fe2-android/app/src/main/res/menu/lao_detail_menu.xml +++ b/fe2-android/app/src/main/res/menu/main_menu.xml @@ -1,24 +1,39 @@ + + + + - + android:id="@+id/main_menu_social_media" + android:icon="@drawable/ic_common_social_media_enabled" + android:title="@string/tab_social_media" /> + + + android:id="@+id/main_menu_witnessing" + android:icon="@drawable/ic_lao_detail_witness_foreground" + android:title="@string/witnessing" /> + + + + diff --git a/fe2-android/app/src/main/res/values/dimens.xml b/fe2-android/app/src/main/res/values/dimens.xml index fc1924a132..17bc151d33 100644 --- a/fe2-android/app/src/main/res/values/dimens.xml +++ b/fe2-android/app/src/main/res/values/dimens.xml @@ -14,7 +14,7 @@ 18sp 14sp 16sp - 12sp + 10sp 10dp 50dp @@ -46,7 +46,6 @@ 8dp - 40dp 20dp 10dp 14sp @@ -98,8 +97,6 @@ 18sp 8dp - 30dp - 20dp 28dp @@ -145,4 +142,8 @@ 12dp 5dp 1dp + + + 20dp + 16dp diff --git a/fe2-android/app/src/main/res/values/strings.xml b/fe2-android/app/src/main/res/values/strings.xml index 72eb888588..6b5ca691e8 100644 --- a/fe2-android/app/src/main/res/values/strings.xml +++ b/fe2-android/app/src/main/res/values/strings.xml @@ -11,14 +11,16 @@ Organizer Attendee Witness + Member - Identity Social Media Digital Cash - Event List + Events Witnessing + Invite Tokens + Disconnect My LAOs @@ -45,10 +47,9 @@ Current Upcoming Previous - Lao\'s QR code - Lao Properties Connected to server Identifier + Name Open @@ -70,23 +71,10 @@ Roll Call Title* - You can participate in organizations and meetings anonymously by leaving this - box checked.\nIf you wish to reveal your identity to other participants in this organization,\nyou may un-check - this box and enter the information you wish to reveal below.\nYou must enter identity information in order to - play an Organizer or Witness role in an organization. - Open Attendee scanned successfully REOPEN - - Name - Title - Email - Organization - Phone - Anonymous - Scan personal QR code of the witness to add Scan a QR code to connect to a LAO @@ -234,9 +222,7 @@ Clearing was unsuccessful - Open Qr code image Qr code image to display LAO connecting information - Close Qr code Status of the event Time icon Expand diff --git a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/InviteFragmentPageObject.java b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/InviteFragmentPageObject.java new file mode 100644 index 0000000000..288c00bd7c --- /dev/null +++ b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/InviteFragmentPageObject.java @@ -0,0 +1,27 @@ +package com.github.dedis.popstellar.testutils.pages.detail; + +import androidx.test.espresso.ViewInteraction; + +import com.github.dedis.popstellar.R; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +public class InviteFragmentPageObject { + + private InviteFragmentPageObject() { + throw new IllegalStateException("Page object"); + } + + public static ViewInteraction roleText() { + return onView(withId(R.id.lao_properties_role_text)); + } + + public static ViewInteraction identifierText() { + return onView(withId(R.id.lao_properties_identifier_text)); + } + + public static ViewInteraction laoNameText() { + return onView(withId(R.id.lao_properties_name_text)); + } +} diff --git a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/LaoDetailActivityPageObject.java b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/LaoDetailActivityPageObject.java index f8c530f219..b8808328d6 100644 --- a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/LaoDetailActivityPageObject.java +++ b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/LaoDetailActivityPageObject.java @@ -8,7 +8,6 @@ import com.github.dedis.popstellar.utility.Constants; import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription; import static androidx.test.espresso.matcher.ViewMatchers.withId; /** @@ -26,41 +25,11 @@ public static ViewInteraction fragmentContainer() { return onView(withId(R.id.fragment_container_lao_detail)); } - public static ViewInteraction tokenButton() { - return onView(withId(R.id.lao_detail_tokens_menu)); - } - - public static ViewInteraction socialMediaButton() { - return onView(withId(R.id.lao_detail_social_media_menu)); - } - - public static ViewInteraction digitalCashButton() { - return onView(withId(R.id.lao_detail_digital_cash_menu)); - } - - public static ViewInteraction witnessButton() { - return onView(withId(R.id.lao_detail_witnessing_menu)); - } - - public static ViewInteraction toolBarBackButton() { - return onView(withContentDescription("Navigate up")); - } - @IdRes public static int laoDetailFragmentId() { return R.id.fragment_lao_detail; } - @IdRes - public static int tokenListFragmentId() { - return R.id.fragment_tokens; - } - - @IdRes - public static int witnessingFragmentId() { - return R.id.fragment_witnessing; - } - @IdRes public static int qrCodeFragmentId() { return R.id.fragment_qrcode; diff --git a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/LaoDetailFragmentPageObject.java b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/LaoDetailFragmentPageObject.java index 6d1dea9b7c..1b80b3968a 100644 --- a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/LaoDetailFragmentPageObject.java +++ b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/detail/LaoDetailFragmentPageObject.java @@ -13,14 +13,6 @@ private LaoDetailFragmentPageObject() { throw new IllegalStateException("Page object"); } - public static ViewInteraction qrCodeLayout() { - return onView(withId(R.id.lao_detail_qr_layout)); - } - - public static ViewInteraction connectQrCode() { - return onView(withId(R.id.channel_qr_code)); - } - public static ViewInteraction addEventButton() { return onView(withId(R.id.add_event)); } @@ -41,10 +33,6 @@ public static ViewInteraction addRollCallText() { return onView(withId(R.id.add_roll_call_text)); } - public static ViewInteraction qrCodeIcon() { - return onView(withId(R.id.lao_toolbar_qr_code)); - } - public static ViewInteraction eventList() { return onView(withId(R.id.event_list)); } diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/InviteFragmentTest.java b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/InviteFragmentTest.java new file mode 100644 index 0000000000..29918afbb9 --- /dev/null +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/InviteFragmentTest.java @@ -0,0 +1,88 @@ +package com.github.dedis.popstellar.ui.detail; + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.github.dedis.popstellar.model.objects.Lao; +import com.github.dedis.popstellar.model.objects.security.KeyPair; +import com.github.dedis.popstellar.model.objects.security.PublicKey; +import com.github.dedis.popstellar.repository.LAORepository; +import com.github.dedis.popstellar.testutils.Base64DataUtils; +import com.github.dedis.popstellar.testutils.BundleBuilder; +import com.github.dedis.popstellar.testutils.fragment.ActivityFragmentScenarioRule; +import com.github.dedis.popstellar.utility.security.KeyManager; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExternalResource; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoTestRule; + +import javax.inject.Inject; + +import dagger.hilt.android.testing.*; + +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static com.github.dedis.popstellar.testutils.pages.detail.InviteFragmentPageObject.*; +import static com.github.dedis.popstellar.testutils.pages.detail.LaoDetailActivityPageObject.*; +import static org.mockito.Mockito.when; + +@SmallTest +@HiltAndroidTest +@RunWith(AndroidJUnit4.class) +public class InviteFragmentTest { + private static final String LAO_NAME = "LAO"; + private static final KeyPair KEY_PAIR = Base64DataUtils.generateKeyPair(); + private static final PublicKey PK = KEY_PAIR.getPublicKey(); + + private static final Lao LAO = new Lao(LAO_NAME, PK, 44444444); + + @Inject LAORepository laoRepository; + + @BindValue @Mock KeyManager keyManager; + + @Rule public InstantTaskExecutorRule rule = new InstantTaskExecutorRule(); + + @Rule(order = 0) + public final MockitoTestRule mockitoRule = MockitoJUnit.testRule(this); + + @Rule(order = 1) + public final HiltAndroidRule hiltRule = new HiltAndroidRule(this); + + @Rule(order = 2) + public final ExternalResource setupRule = + new ExternalResource() { + @Override + protected void before() { + hiltRule.inject(); + + laoRepository.updateLao(LAO); + + when(keyManager.getMainKeyPair()).thenReturn(KEY_PAIR); + when(keyManager.getMainPublicKey()).thenReturn(PK); + } + }; + + @Rule(order = 3) + public ActivityFragmentScenarioRule activityScenarioRule = + ActivityFragmentScenarioRule.launchIn( + LaoDetailActivity.class, + new BundleBuilder() + .putString(laoIdExtra(), LAO.getId()) + .putString(fragmentToOpenExtra(), laoDetailValue()) + .build(), + containerId(), + InviteFragment.class, + InviteFragment::newInstance); + + @Test + public void displayedInfoIsCorrect() { + roleText().check(matches(withText("Organizer"))); + laoNameText().check(matches(withText(LAO_NAME))); + identifierText().check(matches(withText(PK.getEncoded()))); + } +} diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/LaoDetailActivityTest.java b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/LaoDetailActivityTest.java deleted file mode 100644 index 0e1b8be317..0000000000 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/LaoDetailActivityTest.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.github.dedis.popstellar.ui.detail; - -import androidx.test.espresso.intent.Intents; -import androidx.test.ext.junit.rules.ActivityScenarioRule; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.github.dedis.popstellar.model.objects.Lao; -import com.github.dedis.popstellar.model.objects.Wallet; -import com.github.dedis.popstellar.model.objects.view.LaoView; -import com.github.dedis.popstellar.repository.LAORepository; -import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager; -import com.github.dedis.popstellar.testutils.*; -import com.github.dedis.popstellar.ui.digitalcash.DigitalCashActivity; -import com.github.dedis.popstellar.ui.socialmedia.SocialMediaActivity; -import com.github.dedis.popstellar.utility.error.UnknownLaoException; -import com.google.gson.Gson; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.*; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; - -import java.security.GeneralSecurityException; - -import javax.inject.Inject; - -import dagger.hilt.android.testing.*; -import io.reactivex.subjects.BehaviorSubject; - -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.intent.Intents.intended; -import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent; -import static androidx.test.espresso.matcher.ViewMatchers.withChild; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static com.github.dedis.popstellar.testutils.pages.detail.LaoDetailActivityPageObject.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -@HiltAndroidTest -@RunWith(AndroidJUnit4.class) -public class LaoDetailActivityTest { - - private static final Lao LAO = new Lao("LAO", Base64DataUtils.generatePublicKey(), 10223421); - private static final String LAO_ID = LAO.getId(); - - @Inject GlobalNetworkManager networkManager; - @Inject Gson gson; - - @BindValue @Mock LAORepository laoRepository; - @BindValue @Mock Wallet wallet; - - // Hilt rule - private final HiltAndroidRule hiltAndroidRule = new HiltAndroidRule(this); - // Setup rule, used to setup things before the activity is started - private final TestRule setupRule = - new ExternalResource() { - @Override - protected void before() throws UnknownLaoException, GeneralSecurityException { - hiltAndroidRule.inject(); - when(laoRepository.getLaoView(any())).thenAnswer(invocation -> new LaoView(LAO)); - - when(laoRepository.getLaoObservable(anyString())) - .thenReturn(BehaviorSubject.createDefault(new LaoView(LAO))); - - when(wallet.exportSeed()) - .thenReturn( - new String[] { - "jar", - "together", - "minor", - "alley", - "glow", - "hybrid", - "village", - "creek", - "meadow", - "atom", - "travel", - "bracket" - }); - } - }; - // Activity scenario rule that starts the activity. - // It creates a LaoDetailActivity with set extras such that the LAO is used - public ActivityScenarioRule activityScenarioRule = - new ActivityScenarioRule<>( - IntentUtils.createIntent( - LaoDetailActivity.class, - new BundleBuilder() - .putString(laoIdExtra(), LAO_ID) - .putString(fragmentToOpenExtra(), laoDetailValue()) - .build())); - - @Rule - public final RuleChain rule = - RuleChain.outerRule(MockitoJUnit.testRule(this)) - .around(hiltAndroidRule) - .around(setupRule) - .around(activityScenarioRule); - - @Test - public void identityTabOpensIdentityTab() { - tokenButton().perform(click()); - fragmentContainer().check(matches(withChild(withId(tokenListFragmentId())))); - } - - @Test - public void witnessingTabShowsWitnessTab() { - witnessButton().perform(click()); - fragmentContainer().check(matches(withChild(withId(witnessingFragmentId())))); - } - - @Test - public void cancelGoesBackToEventList() { - tokenButton().perform(click()); - toolBarBackButton().perform(click()); - fragmentContainer().check(matches(withChild(withId(laoDetailFragmentId())))); - } - - @Test - public void socialMediaNavOpensSocialMediaActivity() { - Intents.init(); - socialMediaButton().perform(click()); - intended(hasComponent(SocialMediaActivity.class.getName())); - Intents.release(); - } - - @Test - public void digitalCashNavOpensActivity() { - Intents.init(); - digitalCashButton().perform(click()); - intended(hasComponent(DigitalCashActivity.class.getName())); - Intents.release(); - } -} diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/LaoDetailFragmentTest.java b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/LaoDetailFragmentTest.java index 347f292ef4..49adedc470 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/LaoDetailFragmentTest.java +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/detail/LaoDetailFragmentTest.java @@ -18,6 +18,7 @@ import com.github.dedis.popstellar.model.objects.Channel; import com.github.dedis.popstellar.model.objects.Lao; import com.github.dedis.popstellar.model.objects.security.*; +import com.github.dedis.popstellar.model.objects.view.LaoView; import com.github.dedis.popstellar.model.qrcode.ConnectToLao; import com.github.dedis.popstellar.repository.LAORepository; import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager; @@ -135,16 +136,6 @@ protected void before() throws KeyException { .putString(fragmentToOpenExtra(), laoDetailValue()) .build())); - @Test - public void showPropertyButtonShowsConnectQRCode() { - qrCodeIcon().perform(click()); - - qrCodeLayout().check(matches(isDisplayed())); - - String expectedQRCode = gson.toJson(new ConnectToLao(networkManager.getCurrentUrl(), LAO_ID)); - connectQrCode().check(matches(withQrCode(expectedQRCode))); - } - @Test public void addEventButtonIsDisplayedForOrganizer() { addEventButton().check(matches(isDisplayed())); diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/socialmedia/ChirpListAdapterTest.java b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/socialmedia/ChirpListAdapterTest.java index 97b5f84b38..703c7ddcb1 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/socialmedia/ChirpListAdapterTest.java +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/socialmedia/ChirpListAdapterTest.java @@ -9,6 +9,7 @@ import com.github.dedis.popstellar.model.objects.*; import com.github.dedis.popstellar.model.objects.security.*; +import com.github.dedis.popstellar.utility.Constants; import org.junit.Rule; import org.junit.Test; @@ -27,10 +28,20 @@ @HiltAndroidTest @RunWith(AndroidJUnit4.class) public class ChirpListAdapterTest { + private static final long CREATION_TIME = 1631280815; + private static final String LAO_NAME = "laoName"; + + private static final KeyPair SENDER_KEY_1 = generatePoPToken(); + private static final KeyPair SENDER_KEY_2 = generatePoPToken(); + + private static final PublicKey SENDER_1 = SENDER_KEY_1.getPublicKey(); + private static final PublicKey SENDER_2 = SENDER_KEY_2.getPublicKey(); + + private static final String LAO_ID = Lao.generateLaoId(SENDER_1, CREATION_TIME, LAO_NAME); private final Intent intent = new Intent(ApplicationProvider.getApplicationContext(), SocialMediaActivity.class) - .putExtra("OPENED_FROM", ""); + .putExtra(Constants.LAO_ID_EXTRA, LAO_ID); private final ActivityScenarioRule activityScenarioRule = new ActivityScenarioRule<>(intent); @@ -42,16 +53,6 @@ public class ChirpListAdapterTest { private static final MessageID MESSAGE_ID_1 = generateMessageID(); private static final MessageID MESSAGE_ID_2 = generateMessageID(); - private static final KeyPair SENDER_KEY_1 = generatePoPToken(); - private static final KeyPair SENDER_KEY_2 = generatePoPToken(); - - private static final PublicKey SENDER_1 = SENDER_KEY_1.getPublicKey(); - private static final PublicKey SENDER_2 = SENDER_KEY_2.getPublicKey(); - - private static final long CREATION_TIME = 1631280815; - private static final String LAO_NAME = "laoName"; - private static final String LAO_ID = Lao.generateLaoId(SENDER_1, CREATION_TIME, LAO_NAME); - private static final Channel CHIRP_CHANNEL_1 = Channel.getLaoChannel(LAO_ID).subChannel("social").subChannel(SENDER_1.getEncoded()); private static final Channel CHIRP_CHANNEL_2 =