diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..25c2334 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 43f699e..ad28876 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,6 +42,9 @@ dependencies { implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar' implementation 'com.google.zxing:core:3.4.0' + // external + implementation 'me.saket:inboxrecyclerview:1.0.0-rc1' + implementation fileTree(dir: 'libs', include: ['*.jar']) testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 48b8f7d..1f3a32c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,10 +1,11 @@ - - - + + - - - + - + - + - + + android:label="@string/login"/> + + + + android:parentActivityName=".activities.HomeActivity" + android:screenOrientation="portrait" + android:theme="@style/AppTheme.Translucent"/> + + + + + + android:theme="@style/AppTheme.Translucent"/> \ No newline at end of file diff --git a/app/src/main/java/lu/circl/mispbump/RecyclerViewItemTransition.java b/app/src/main/java/lu/circl/mispbump/RecyclerViewItemTransition.java deleted file mode 100644 index 8b1e789..0000000 --- a/app/src/main/java/lu/circl/mispbump/RecyclerViewItemTransition.java +++ /dev/null @@ -1,54 +0,0 @@ -package lu.circl.mispbump; - -import android.animation.Animator; -import android.animation.ValueAnimator; -import android.transition.Transition; -import android.transition.TransitionValues; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - - -public class RecyclerViewItemTransition extends Transition { - - private static final String PROPNAME_ELEVATION = "customtransition:change_elevation:toolbar"; - - private void captureTransitionValues(TransitionValues transitionValues) { - transitionValues.values.put(PROPNAME_ELEVATION, transitionValues.view.getElevation()); - } - - @Override - public void captureStartValues(@NonNull TransitionValues transitionValues) { - captureTransitionValues(transitionValues); - } - - @Override - public void captureEndValues(@NonNull TransitionValues transitionValues) { - captureTransitionValues(transitionValues); - } - - @Override - public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { - if (null == startValues || null == endValues) { - return null; - } - - final View view = endValues.view; - - int startElevation = 0; - int endElevation = 6; - - ValueAnimator anim = ValueAnimator.ofFloat(startElevation, endElevation); - - anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = (float) animation.getAnimatedValue(); - view.setElevation(t); - } - }); - - return anim; - } -} diff --git a/app/src/main/java/lu/circl/mispbump/activities/HomeActivity.java b/app/src/main/java/lu/circl/mispbump/activities/HomeActivity.java index 9ddae93..7b97cd6 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/HomeActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/HomeActivity.java @@ -2,12 +2,12 @@ package lu.circl.mispbump.activities; import android.content.Intent; import android.os.Bundle; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; -import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityOptionsCompat; @@ -15,7 +15,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.gson.Gson; import java.util.List; @@ -27,11 +26,13 @@ import lu.circl.mispbump.models.UploadInformation; public class HomeActivity extends AppCompatActivity { - private View rootView; - private PreferenceManager preferenceManager; + public static String EXTRA_UPLOAD_INFO = "uploadInformation"; + private List uploadInformationList; + private PreferenceManager preferenceManager; private RecyclerView recyclerView; private UploadInfoAdapter uploadInfoAdapter; + private TextView emptyRecyclerView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -40,76 +41,10 @@ public class HomeActivity extends AppCompatActivity { preferenceManager = PreferenceManager.getInstance(this); - init(); + initViews(); initRecyclerView(); } - - private void init() { - rootView = findViewById(R.id.rootLayout); - - // populate Toolbar (Actionbar) - Toolbar myToolbar = findViewById(R.id.toolbar); - setSupportActionBar(myToolbar); - - ActionBar ab = getSupportActionBar(); - if (ab != null) { - ab.setDisplayHomeAsUpEnabled(false); - } - - FloatingActionButton sync_fab = findViewById(R.id.home_fab); - sync_fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startActivity(new Intent(HomeActivity.this, SyncActivity.class)); - } - }); - } - - private void initRecyclerView() { - recyclerView = findViewById(R.id.recyclerView); - recyclerView.setLayoutManager(new LinearLayoutManager(HomeActivity.this)); - - uploadInfoAdapter = new UploadInfoAdapter(HomeActivity.this); - - uploadInfoAdapter.setOnRecyclerItemClickListener(new OnRecyclerItemClickListener() { - @Override - public void onClick(final View v, UploadInformation item) { - Intent i = new Intent(HomeActivity.this, UploadInformationActivity.class); - i.putExtra(UploadInformationActivity.EXTRA_UPLOAD_INFO_KEY, new Gson().toJson(item)); - - ActivityOptionsCompat options = ActivityOptionsCompat.makeClipRevealAnimation(v.findViewById(R.id.rootLayout), (int) v.getX(), (int) v.getY(), v.getWidth(), v.getHeight()); - startActivity(i, options.toBundle()); - } - }); - - recyclerView.setAdapter(uploadInfoAdapter); - } - - private void refreshRecyclerView() { - List uploadInformationList = preferenceManager.getUploadInformation(); - TextView empty = findViewById(R.id.empty); - - // no sync information available - if (uploadInformationList == null) { - empty.setVisibility(View.VISIBLE); - recyclerView.setVisibility(View.GONE); - return; - } - - // sync information available - empty.setVisibility(View.GONE); - recyclerView.setVisibility(View.VISIBLE); - uploadInfoAdapter.setItems(uploadInformationList); - } - - - @Override - protected void onResume() { - super.onResume(); - refreshRecyclerView(); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_menu, menu); @@ -131,4 +66,63 @@ public class HomeActivity extends AppCompatActivity { // invoke superclass to handle unrecognized item (eg. homeAsUp) return super.onOptionsItemSelected(item); } + + @Override + protected void onResume() { + super.onResume(); + + Log.d("DEBUG", "onResume()"); + + refreshRecyclerView(); + } + + + private void initViews() { + emptyRecyclerView = findViewById(R.id.empty); + + Toolbar myToolbar = findViewById(R.id.toolbar); + setSupportActionBar(myToolbar); + + FloatingActionButton syncFab = findViewById(R.id.home_fab); + syncFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(new Intent(HomeActivity.this, SyncActivity.class)); + } + }); + } + + private void initRecyclerView() { + recyclerView = findViewById(R.id.recyclerView); + recyclerView.setLayoutManager(new LinearLayoutManager(HomeActivity.this)); + uploadInfoAdapter = new UploadInfoAdapter(HomeActivity.this); + uploadInfoAdapter.setOnRecyclerPositionClickListener(onRecyclerItemClickListener()); + recyclerView.setAdapter(uploadInfoAdapter); + } + + private void refreshRecyclerView() { + uploadInformationList = preferenceManager.getUploadInformationList(); + + if (uploadInformationList.isEmpty()) { + emptyRecyclerView.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.GONE); + } else { + emptyRecyclerView.setVisibility(View.GONE); + recyclerView.setVisibility(View.VISIBLE); + uploadInfoAdapter.setItems(uploadInformationList); + } + } + + private OnRecyclerItemClickListener onRecyclerItemClickListener() { + return new OnRecyclerItemClickListener() { + @Override + public void onClick(View v, Integer index) { + Intent i = new Intent(HomeActivity.this, UploadInfoActivity.class); + i.putExtra(UploadInfoActivity.EXTRA_UPLOAD_INFO_UUID, uploadInformationList.get(index).getUuid()); + + ActivityOptionsCompat options = ActivityOptionsCompat.makeClipRevealAnimation(v.findViewById(R.id.rootLayout), (int) v.getX(), (int) v.getY(), v.getWidth(), v.getHeight()); + startActivity(i, options.toBundle()); + } + }; + } } diff --git a/app/src/main/java/lu/circl/mispbump/activities/StartUpActivity.java b/app/src/main/java/lu/circl/mispbump/activities/StartUpActivity.java index af341b9..3eda757 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/StartUpActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/StartUpActivity.java @@ -7,6 +7,10 @@ import androidx.appcompat.app.AppCompatActivity; import lu.circl.mispbump.auxiliary.PreferenceManager; import lu.circl.mispbump.models.restModels.User; +/** + * This activity navigates to the next activity base on the user status. + * This is the first activity that gets loaded when the user starts the app. + */ public class StartUpActivity extends AppCompatActivity { @Override diff --git a/app/src/main/java/lu/circl/mispbump/activities/SyncActivity.java b/app/src/main/java/lu/circl/mispbump/activities/SyncActivity.java index 0ec56f0..472df9f 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/SyncActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/SyncActivity.java @@ -1,13 +1,11 @@ package lu.circl.mispbump.activities; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.content.Intent; +import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.os.Bundle; import android.view.View; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.BounceInterpolator; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; @@ -17,7 +15,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.snackbar.Snackbar; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; @@ -25,309 +22,308 @@ import com.google.gson.JsonSyntaxException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; +import java.util.List; import lu.circl.mispbump.R; +import lu.circl.mispbump.auxiliary.DialogManager; import lu.circl.mispbump.auxiliary.PreferenceManager; import lu.circl.mispbump.auxiliary.QrCodeGenerator; import lu.circl.mispbump.auxiliary.RandomString; import lu.circl.mispbump.fragments.CameraFragment; -import lu.circl.mispbump.customViews.ExtendedBottomSheetBehavior; -import lu.circl.mispbump.fragments.SyncOptionsFragment; +import lu.circl.mispbump.fragments.UploadSettingsFragment; import lu.circl.mispbump.models.SyncInformation; import lu.circl.mispbump.models.UploadInformation; import lu.circl.mispbump.security.DiffieHellman; -/** - * This class provides the sync functionality. - * It collects the necessary information, guides through the process and finally completes with - * the upload to the misp instance. - */ public class SyncActivity extends AppCompatActivity { - // rootLayout - private CoordinatorLayout rootLayout; - private ImageView qrCodeView, bottomSheetIcon; - private TextView bottomSheetText; - private ImageButton prevButton, nextButton; - private ExtendedBottomSheetBehavior bottomSheetBehavior; + enum SyncState { + PUBLIC_KEY, + DATA + } + + enum UiState { + PUBLIC_KEY_SHOW, + PUBLIC_KEY_SHOW_AND_RECEIVED, + SYNC_INFO_SHOW, + SYNC_INFO_SHOW_AND_RECEIVED + } + + private SyncState currentSyncState; - // dependencies private PreferenceManager preferenceManager; + private UploadInformation uploadInformation; + private QrCodeGenerator qrCodeGenerator; private DiffieHellman diffieHellman; - private UploadInformation uploadInformation; + private boolean foreignPublicKeyReceived, foreignSyncInfoReceived; - // fragments + // Fragments private CameraFragment cameraFragment; - private SyncOptionsFragment syncOptionsFragment; - // qr codes - private QrCodeGenerator qrCodeGenerator; - private Bitmap publicKeyQr, syncInfoQr; + // Views + private CoordinatorLayout rootLayout; + private FrameLayout qrFrame; + private ImageView qrCode; + private TextView qrHint; + private ImageButton prevButton, nextButton; - private SyncState currentSyncState = SyncState.settings; + private Bitmap publicKeyQrCode, syncInfoQrCode; - private enum SyncState { - settings(0), - publicKeyExchange(1), - dataExchange(2); - - - private final int value; - - SyncState(final int value) { - this.value = value; - } - } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sync); - initializeViews(); + + init(); + initViews(); + + switchState(SyncState.PUBLIC_KEY); } - private void initializeViews() { + @Override + public void onBackPressed() { + super.onBackPressed(); + switch (currentSyncState) { + case PUBLIC_KEY: + // TODO warn that sync is maybe not complete ... ? + break; + case DATA: + switchState(SyncState.PUBLIC_KEY); + break; + } + } + + private void init() { + preferenceManager = PreferenceManager.getInstance(SyncActivity.this); + diffieHellman = DiffieHellman.getInstance(); + + qrCodeGenerator = new QrCodeGenerator(SyncActivity.this); + publicKeyQrCode = qrCodeGenerator.generateQrCode(DiffieHellman.publicKeyToString(diffieHellman.getPublicKey())); + + uploadInformation = new UploadInformation(); + } + + private void initViews() { rootLayout = findViewById(R.id.rootLayout); - // prev button + qrFrame = findViewById(R.id.qrFrame); + qrCode = findViewById(R.id.qrCode); + qrHint = findViewById(R.id.qrHint); + prevButton = findViewById(R.id.prevButton); prevButton.setOnClickListener(onPrevClicked); - // next button nextButton = findViewById(R.id.nextButton); nextButton.setOnClickListener(onNextClicked); - - // QR Code View - qrCodeView = findViewById(R.id.qrcode); - qrCodeGenerator = new QrCodeGenerator(SyncActivity.this); - - bottomSheetIcon = findViewById(R.id.bottomSheetIcon); - bottomSheetText = findViewById(R.id.bottomSheetText); - - diffieHellman = DiffieHellman.getInstance(); - preferenceManager = PreferenceManager.getInstance(this); - - View bottomSheet = findViewById(R.id.bottomSheet); - bottomSheetBehavior = (ExtendedBottomSheetBehavior) BottomSheetBehavior.from(bottomSheet); - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - bottomSheetBehavior.setSwipeable(false); - bottomSheetBehavior.setHideable(false); - - publicKeyQr = generatePublicKeyQr(); - - switchState(SyncState.settings); } - /** - * Called when "next button" is pressed - */ - private View.OnClickListener onNextClicked = new View.OnClickListener() { - @Override - public void onClick(View v) { - switch (currentSyncState) { - case settings: - uploadInformation = new UploadInformation(); - uploadInformation.setAllowSelfSigned(syncOptionsFragment.getAllowSelfSigned()); - uploadInformation.setPush(syncOptionsFragment.getPush()); - uploadInformation.setPull(syncOptionsFragment.getPull()); - uploadInformation.setCached(syncOptionsFragment.getCache()); + private void switchState(SyncState state) { + switchFragment(state); + displayQr(state); - switchState(SyncState.publicKeyExchange); - break; - - case publicKeyExchange: - switchState(SyncState.dataExchange); - break; - - case dataExchange: - Intent upload = new Intent(SyncActivity.this, UploadActivity.class); - upload.putExtra(UploadActivity.EXTRA_UPLOAD_INFO, new Gson().toJson(uploadInformation)); - startActivity(upload); - overridePendingTransition(R.anim.slide_in_right, android.R.anim.slide_out_right); - finish(); - break; - } + switch (state) { + case PUBLIC_KEY: + if (foreignPublicKeyReceived) { + switchUiState(UiState.PUBLIC_KEY_SHOW_AND_RECEIVED); + cameraFragment.setReadQrEnabled(false); + } else { + switchUiState(UiState.PUBLIC_KEY_SHOW); + cameraFragment.setReadQrEnabled(true); + } + break; + case DATA: + if (foreignSyncInfoReceived) { + switchUiState(UiState.SYNC_INFO_SHOW_AND_RECEIVED); + cameraFragment.setReadQrEnabled(false); + } else { + switchUiState(UiState.SYNC_INFO_SHOW); + cameraFragment.setReadQrEnabled(true); + } + break; } - }; - /** - * Called when "prev button" is clicked - */ + currentSyncState = state; + } + + private void switchFragment(SyncState state) { + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + + String camTag = CameraFragment.class.getSimpleName(); + String settingsTag = UploadSettingsFragment.class.getSimpleName(); + + switch (state) { + case PUBLIC_KEY: + case DATA: + cameraFragment = (CameraFragment) fragmentManager.findFragmentByTag(camTag); + if (cameraFragment != null) { + fragmentTransaction.show(cameraFragment); + } else { + cameraFragment = new CameraFragment(); + cameraFragment.setOnQrAvailableListener(onReadQrCode); + fragmentTransaction.add(R.id.fragmentContainer, cameraFragment, camTag); + } + + UploadSettingsFragment uploadSettingsFragment = (UploadSettingsFragment) fragmentManager.findFragmentByTag(settingsTag); + if (uploadSettingsFragment != null) { + fragmentTransaction.hide(uploadSettingsFragment); + } + + fragmentTransaction.commit(); + break; + } + } + + private void displayQr(SyncState state) { + switch (state) { + case PUBLIC_KEY: + qrCode.setImageBitmap(publicKeyQrCode); + qrFrame.setVisibility(View.VISIBLE); + break; + case DATA: + qrCode.setImageBitmap(syncInfoQrCode); + qrFrame.setVisibility(View.VISIBLE); + break; + } + } + + private void switchUiState(final UiState state) { + runOnUiThread(new Runnable() { + @Override + public void run() { + switch (state) { + case PUBLIC_KEY_SHOW: + prevButton.setImageDrawable(getDrawable(R.drawable.ic_close)); + prevButton.setVisibility(View.VISIBLE); + nextButton.setVisibility(View.INVISIBLE); + qrReceivedFeedback(false); + break; + case PUBLIC_KEY_SHOW_AND_RECEIVED: + prevButton.setImageDrawable(getDrawable(R.drawable.ic_close)); + nextButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_forward)); + prevButton.setVisibility(View.VISIBLE); + nextButton.setVisibility(View.VISIBLE); + cameraFragment.disablePreview(); + qrReceivedFeedback(true); + break; + case SYNC_INFO_SHOW: + prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back)); + nextButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_forward)); + nextButton.setVisibility(View.INVISIBLE); + prevButton.setVisibility(View.VISIBLE); + cameraFragment.enablePreview(); + qrReceivedFeedback(false); + break; + case SYNC_INFO_SHOW_AND_RECEIVED: + prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back)); + nextButton.setImageDrawable(getDrawable(R.drawable.ic_check)); + nextButton.setVisibility(View.VISIBLE); + prevButton.setVisibility(View.VISIBLE); + cameraFragment.disablePreview(); + qrReceivedFeedback(true); + break; + } + } + }); + } + + // listener + private View.OnClickListener onPrevClicked = new View.OnClickListener() { @Override public void onClick(View v) { switch (currentSyncState) { - case settings: + case PUBLIC_KEY: finish(); break; - - case publicKeyExchange: - switchState(SyncState.settings); - break; - - case dataExchange: - switchState(SyncState.publicKeyExchange); + case DATA: + switchState(SyncState.PUBLIC_KEY); break; } } }; - /** - * Called when the camera fragment detects a qr code - */ - private CameraFragment.QrScanCallback onQrCodeScanned = new CameraFragment.QrScanCallback() { + private View.OnClickListener onNextClicked = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (currentSyncState) { + case PUBLIC_KEY: + switchState(SyncState.DATA); + break; + + case DATA: + uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.PENDING); + preferenceManager.addUploadInformation(uploadInformation); + + Intent i = new Intent(SyncActivity.this, UploadInfoActivity.class); + i.putExtra(UploadInfoActivity.EXTRA_UPLOAD_INFO_UUID, uploadInformation.getUuid()); + startActivity(i); + finish(); + break; + } + } + }; + + private CameraFragment.QrScanCallback onReadQrCode = new CameraFragment.QrScanCallback() { @Override public void qrScanResult(String qrData) { cameraFragment.setReadQrEnabled(false); switch (currentSyncState) { - case publicKeyExchange: + case PUBLIC_KEY: try { final PublicKey pk = DiffieHellman.publicKeyFromString(qrData); diffieHellman.setForeignPublicKey(pk); - - syncInfoQr = generateSyncInfoQr(); - - runOnUiThread(new Runnable() { - @Override - public void run() { - nextButton.setVisibility(View.VISIBLE); - cameraFragment.disablePreview(); - qrReceivedFeedback(); - } - }); + syncInfoQrCode = generateSyncInfoQr(); + switchUiState(UiState.PUBLIC_KEY_SHOW_AND_RECEIVED); + foreignPublicKeyReceived = true; } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { Snackbar.make(rootLayout, "Invalid key", Snackbar.LENGTH_SHORT).show(); - cameraFragment.setReadQrEnabled(true); + switchUiState(UiState.PUBLIC_KEY_SHOW); } break; - case dataExchange: - cameraFragment.setReadQrEnabled(false); - + case DATA: try { final SyncInformation remoteSyncInfo = new Gson().fromJson(diffieHellman.decrypt(qrData), SyncInformation.class); - uploadInformation.setRemote(remoteSyncInfo); - runOnUiThread(new Runnable() { - @Override - public void run() { - cameraFragment.disablePreview(); - nextButton.setVisibility(View.VISIBLE); - qrReceivedFeedback(); + List uploadInformationList = preferenceManager.getUploadInformationList(); + + if (uploadInformationList != null) { + for (final UploadInformation ui : uploadInformationList) { + if (ui.getRemote().organisation.uuid.equals(remoteSyncInfo.organisation.uuid)) { + DialogManager.syncAlreadyExistsDialog(SyncActivity.this, new DialogManager.IDialogFeedback() { + @Override + public void positive() { + uploadInformation.setUuid(ui.getUuid()); + } + + @Override + public void negative() { + finish(); + } + }); + } } - }); + } + uploadInformation.setRemote(remoteSyncInfo); + switchUiState(UiState.SYNC_INFO_SHOW_AND_RECEIVED); + foreignSyncInfoReceived = true; } catch (JsonSyntaxException e) { Snackbar.make(rootLayout, "Sync information unreadable", Snackbar.LENGTH_SHORT).show(); - cameraFragment.setReadQrEnabled(true); + switchUiState(UiState.SYNC_INFO_SHOW); } break; } } }; + // aux - private void switchUiState(SyncState state) { - - bottomSheetIcon.setVisibility(View.INVISIBLE); - bottomSheetBehavior.setSwipeable(false); - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); - - switch (state) { - case settings: - prevButton.setImageDrawable(getDrawable(R.drawable.ic_close)); - prevButton.setVisibility(View.VISIBLE); - nextButton.setVisibility(View.VISIBLE); - hideQrCode(); - break; - case publicKeyExchange: - prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back)); - prevButton.setVisibility(View.VISIBLE); - - nextButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_forward)); - nextButton.setVisibility(View.GONE); - showQrCode(publicKeyQr); - break; - case dataExchange: - prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back)); - prevButton.setVisibility(View.VISIBLE); - - nextButton.setImageDrawable(getDrawable(R.drawable.ic_cloud_upload)); - nextButton.setVisibility(View.GONE); - - cameraFragment.enablePreview(); - cameraFragment.setReadQrEnabled(true); - showQrCode(syncInfoQr); - break; - } - } - - private void switchState(SyncState state) { - - FragmentManager fragmentManager = getSupportFragmentManager(); - FragmentTransaction transaction = fragmentManager.beginTransaction(); - - if (currentSyncState != state) { - if (state.value < currentSyncState.value) { - transaction.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right); - } else { - transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left); - } - } - - currentSyncState = state; - - switchUiState(currentSyncState); - - switch (currentSyncState) { - case settings: - String fragTag = SyncOptionsFragment.class.getSimpleName(); - syncOptionsFragment = (SyncOptionsFragment) fragmentManager.findFragmentByTag(fragTag); - - if (syncOptionsFragment == null) { - syncOptionsFragment = new SyncOptionsFragment(); - } - - transaction.replace(R.id.sync_fragment_container, syncOptionsFragment, fragTag); - transaction.commit(); - break; - - case publicKeyExchange: - fragTag = CameraFragment.class.getSimpleName(); - cameraFragment = (CameraFragment) fragmentManager.findFragmentByTag(fragTag); - - if (cameraFragment == null) { - cameraFragment = new CameraFragment(); - cameraFragment.setOnQrAvailableListener(onQrCodeScanned); - } - - transaction.replace(R.id.sync_fragment_container, cameraFragment, fragTag); - transaction.commit(); - break; - - case dataExchange: - fragTag = CameraFragment.class.getSimpleName(); - cameraFragment = (CameraFragment) fragmentManager.findFragmentByTag(fragTag); - - if (cameraFragment == null) { - cameraFragment = new CameraFragment(); - cameraFragment.setOnQrAvailableListener(onQrCodeScanned); - } - - transaction.replace(R.id.sync_fragment_container, cameraFragment, fragTag); - transaction.commit(); - break; - } - } - - - private Bitmap generatePublicKeyQr() { - return qrCodeGenerator.generateQrCode(DiffieHellman.publicKeyToString(diffieHellman.getPublicKey())); - } - - private Bitmap generateSyncInfoQr() { + private SyncInformation generateLocalSyncInfo() { SyncInformation syncInformation = new SyncInformation(); syncInformation.organisation = preferenceManager.getUserOrganisation().toSyncOrganisation(); syncInformation.syncUserAuthkey = new RandomString(40).nextString(); @@ -335,6 +331,12 @@ public class SyncActivity extends AppCompatActivity { syncInformation.syncUserPassword = new RandomString(16).nextString(); syncInformation.syncUserEmail = preferenceManager.getUserInfo().email; + return syncInformation; + } + + private Bitmap generateSyncInfoQr() { + SyncInformation syncInformation = generateLocalSyncInfo(); + uploadInformation.setLocal(syncInformation); // encrypt serialized content @@ -344,80 +346,18 @@ public class SyncActivity extends AppCompatActivity { return qrCodeGenerator.generateQrCode(encrypted); } - - private void showQrCode(final Bitmap bitmap) { + private void qrReceivedFeedback(final boolean done) { runOnUiThread(new Runnable() { @Override public void run() { - - qrCodeView.setImageBitmap(bitmap); - qrCodeView.setAlpha(0f); - qrCodeView.setVisibility(View.VISIBLE); - qrCodeView.setScaleX(0.9f); - qrCodeView.setScaleY(0.6f); - qrCodeView.animate() - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .setDuration(250) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - qrCodeView.setVisibility(View.VISIBLE); - } - }); + if (done) { + qrHint.setCompoundDrawablesWithIntrinsicBounds(getDrawable(R.drawable.ic_check_outline), null, null, null); + qrHint.setCompoundDrawableTintList(ColorStateList.valueOf(getColor(R.color.status_green))); + } else { + qrHint.setCompoundDrawablesWithIntrinsicBounds(getDrawable(R.drawable.ic_info_outline), null, null, null); + qrHint.setCompoundDrawableTintList(ColorStateList.valueOf(getColor(R.color.status_amber))); + } } }); } - - private void hideQrCode() { - - if (qrCodeView.getVisibility() == View.GONE) { - return; - } - - runOnUiThread(new Runnable() { - @Override - public void run() { - qrCodeView.setAlpha(1f); - qrCodeView.setVisibility(View.VISIBLE); - qrCodeView.setScaleX(1f); - qrCodeView.setScaleY(1f); - qrCodeView.animate() - .scaleX(0f) - .scaleY(0f) - .alpha(0f) - .setDuration(250) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - qrCodeView.setVisibility(View.GONE); - } - }); - } - }); - } - - private void qrReceivedFeedback() { - bottomSheetIcon.setScaleX(0f); - bottomSheetIcon.setScaleY(0f); - bottomSheetIcon.setVisibility(View.VISIBLE); - bottomSheetIcon.animate() - .scaleY(1f) - .scaleX(1f) - .setDuration(500); - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); - bottomSheetBehavior.setSwipeable(true); - - switch (currentSyncState) { - case publicKeyExchange: - bottomSheetText.setText("Received public key from partner"); - break; - - case dataExchange: - bottomSheetText.setText("Received sync information from partner"); - break; - } - } - } diff --git a/app/src/main/java/lu/circl/mispbump/activities/UploadActivity.java b/app/src/main/java/lu/circl/mispbump/activities/UploadActivity.java index cbb8dc3..7bdcb49 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/UploadActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/UploadActivity.java @@ -1,5 +1,6 @@ package lu.circl.mispbump.activities; +import android.content.Intent; import android.os.Bundle; import android.view.MenuItem; import android.view.View; @@ -9,9 +10,7 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.coordinatorlayout.widget.CoordinatorLayout; -import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; -import com.google.gson.Gson; import java.io.IOException; import java.util.List; @@ -29,34 +28,67 @@ import lu.circl.mispbump.models.restModels.User; public class UploadActivity extends AppCompatActivity { - public static final String EXTRA_UPLOAD_INFO = "uploadInformation"; + public static String EXTRA_UPLOAD_INFO = "uploadInformation"; private PreferenceManager preferenceManager; - private MispRestClient restClient; private UploadInformation uploadInformation; private CoordinatorLayout rootLayout; - + private MispRestClient restClient; private UploadAction availableAction, orgAction, userAction, serverAction; + private boolean errorWhileUpload; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_upload); + + preferenceManager = PreferenceManager.getInstance(UploadActivity.this); + restClient = MispRestClient.getInstance(this); + parseExtra(); - init(); + initViews(); + startUpload(); } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + if (item.getItemId() == android.R.id.home) { + saveCurrentState(); + finish(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + saveCurrentState(); + } + + private void parseExtra() { - String uploadInfoString = getIntent().getStringExtra(EXTRA_UPLOAD_INFO); - uploadInformation = new Gson().fromJson(uploadInfoString, UploadInformation.class); - assert uploadInformation != null; + Intent i = getIntent(); + + UUID currentUUID = (UUID) i.getSerializableExtra(EXTRA_UPLOAD_INFO); + + for (UploadInformation ui : preferenceManager.getUploadInformationList()) { + if (ui.getUuid().compareTo(currentUUID) == 0) { + uploadInformation = ui; + return; + } + } + + if (uploadInformation == null) { + throw new RuntimeException("Could not find UploadInfo with UUID {" + currentUUID.toString() + "}"); + } } - private void init() { - restClient = MispRestClient.getInstance(this); - preferenceManager = PreferenceManager.getInstance(this); - + private void initViews() { rootLayout = findViewById(R.id.rootLayout); // toolbar @@ -68,32 +100,17 @@ public class UploadActivity extends AppCompatActivity { ab.setDisplayHomeAsUpEnabled(true); ab.setHomeAsUpIndicator(R.drawable.ic_close); - // fab - FloatingActionButton fab = findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startUpload(); - } - }); - availableAction = findViewById(R.id.availableAction); orgAction = findViewById(R.id.orgAction); userAction = findViewById(R.id.userAction); serverAction = findViewById(R.id.serverAction); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - preferenceManager.addUploadInformation(uploadInformation); - finish(); - return true; - - default: - return super.onOptionsItemSelected(item); + private void saveCurrentState() { + if (errorWhileUpload) { + uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE); } + preferenceManager.addUploadInformation(uploadInformation); } /** @@ -122,6 +139,7 @@ public class UploadActivity extends AppCompatActivity { return syncUser; } + private MispRestClient.AvailableCallback availableCallback = new MispRestClient.AvailableCallback() { @Override public void available() { @@ -136,12 +154,15 @@ public class UploadActivity extends AppCompatActivity { availableAction.setError(error); uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE); + errorWhileUpload = true; + Snackbar sb = Snackbar.make(rootLayout, error, Snackbar.LENGTH_INDEFINITE); sb.setAction("Retry", new View.OnClickListener() { @Override public void onClick(View v) { availableAction.setError(null); availableAction.setCurrentUploadState(UploadAction.UploadState.LOADING); + errorWhileUpload = false; startUpload(); } }); @@ -165,9 +186,9 @@ public class UploadActivity extends AppCompatActivity { public void failure(String error) { orgAction.setCurrentUploadState(UploadAction.UploadState.ERROR); orgAction.setError(error); + errorWhileUpload = true; uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE); - preferenceManager.addUploadInformation(uploadInformation); } }; @@ -194,9 +215,9 @@ public class UploadActivity extends AppCompatActivity { public void failure(String error) { userAction.setCurrentUploadState(UploadAction.UploadState.ERROR); userAction.setError(error); + errorWhileUpload = true; uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE); - preferenceManager.addUploadInformation(uploadInformation); } }; @@ -210,17 +231,16 @@ public class UploadActivity extends AppCompatActivity { public void success(Server server) { serverAction.setCurrentUploadState(UploadAction.UploadState.DONE); uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.COMPLETE); - preferenceManager.addUploadInformation(uploadInformation); - finish(); + saveCurrentState(); } @Override public void failure(String error) { serverAction.setCurrentUploadState(UploadAction.UploadState.ERROR); serverAction.setError(error); + errorWhileUpload = true; uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE); - preferenceManager.addUploadInformation(uploadInformation); } }; diff --git a/app/src/main/java/lu/circl/mispbump/activities/UploadInfoActivity.java b/app/src/main/java/lu/circl/mispbump/activities/UploadInfoActivity.java new file mode 100644 index 0000000..71dec20 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/activities/UploadInfoActivity.java @@ -0,0 +1,243 @@ +package lu.circl.mispbump.activities; + +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.tabs.TabLayout; + +import java.util.UUID; + +import lu.circl.mispbump.R; +import lu.circl.mispbump.auxiliary.PreferenceManager; +import lu.circl.mispbump.fragments.UploadInfoFragment; +import lu.circl.mispbump.fragments.UploadSettingsFragment; +import lu.circl.mispbump.models.UploadInformation; + +public class UploadInfoActivity extends AppCompatActivity { + + public static String EXTRA_UPLOAD_INFO_UUID = "uploadInformation"; + + private PreferenceManager preferenceManager; + private UploadInformation uploadInformation; + private ViewPagerAdapter viewPagerAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_upload_information_2); + + preferenceManager = PreferenceManager.getInstance(UploadInfoActivity.this); + + // tint statusBar + getWindow().setStatusBarColor(getColor(R.color.grey_light)); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + + parseExtra(); + initToolbar(); + initViewPager(); + initViews(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_upload_info, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.delete: + // TODO delete + return true; + + case android.R.id.home: + saveCurrentSettings(); + finish(); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + protected void onResume() { + super.onResume(); + + // refresh current uploadInformation + if (uploadInformation != null) { + uploadInformation = preferenceManager.getUploadInformation(uploadInformation.getUuid()); + } + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + saveCurrentSettings(); + } + + + private void parseExtra() { + Intent i = getIntent(); + + UUID currentUUID = (UUID) i.getSerializableExtra(EXTRA_UPLOAD_INFO_UUID); + uploadInformation = preferenceManager.getUploadInformation(currentUUID); + + if (uploadInformation == null) { + throw new RuntimeException("Could not find UploadInformation with UUID {" + currentUUID.toString() + "}"); + } + } + + private void initToolbar() { + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + ActionBar ab = getSupportActionBar(); + assert ab != null; + + + TextView tv = findViewById(R.id.syncStatus); + int statusColor; + String statusText; + Drawable statusDrawable; + + switch (uploadInformation.getCurrentSyncStatus()) { + case COMPLETE: + statusColor = getColor(R.color.status_green); + statusText = "Successfully uploaded"; + statusDrawable = getDrawable(R.drawable.ic_check_outline); + break; + + case FAILURE: + statusColor = getColor(R.color.status_red); + statusText = "Error while uploading"; + statusDrawable = getDrawable(R.drawable.ic_error_outline); + break; + + case PENDING: + statusColor = getColor(R.color.status_amber); + statusText = "Not uploaded yet"; + statusDrawable = getDrawable(R.drawable.ic_pending); + break; + + default: + statusColor = getColor(R.color.status_green); + statusText = "Successfully uploaded"; + statusDrawable = getDrawable(R.drawable.ic_check_outline); + break; + } + + tv.setText(statusText); + tv.setTextColor(statusColor); + tv.setCompoundDrawablesWithIntrinsicBounds(null, null, statusDrawable, null); + tv.setCompoundDrawableTintList(ColorStateList.valueOf(statusColor)); + + ab.setTitle(uploadInformation.getRemote().organisation.name); + + ab.setDisplayShowTitleEnabled(true); + ab.setDisplayShowCustomEnabled(false); + ab.setDisplayHomeAsUpEnabled(true); + } + + private void initViewPager() { + ViewPager viewPager = findViewById(R.id.viewPager); + TabLayout tabLayout = findViewById(R.id.tabLayout); + + viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(), uploadInformation); + + viewPager.setAdapter(viewPagerAdapter); + tabLayout.setupWithViewPager(viewPager); + } + + private void initViews() { + FloatingActionButton fab = findViewById(R.id.fab); + + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + saveCurrentSettings(); + + Intent i = new Intent(UploadInfoActivity.this, UploadActivity.class); + i.putExtra(UploadActivity.EXTRA_UPLOAD_INFO, uploadInformation.getUuid()); + startActivity(i); + } + }); + } + + private void saveCurrentSettings() { + uploadInformation.setAllowSelfSigned(viewPagerAdapter.uploadSettingsFragment.getAllowSelfSigned()); + uploadInformation.setPull(viewPagerAdapter.uploadSettingsFragment.getPull()); + uploadInformation.setPush(viewPagerAdapter.uploadSettingsFragment.getPush()); + uploadInformation.setCached(viewPagerAdapter.uploadSettingsFragment.getCache()); + + preferenceManager.addUploadInformation(uploadInformation); + } + + + class ViewPagerAdapter extends FragmentPagerAdapter { + + private UploadSettingsFragment uploadSettingsFragment; + private UploadInfoFragment uploadInfoFragment = new UploadInfoFragment(); + + ViewPagerAdapter(@NonNull FragmentManager fm, UploadInformation uploadInformation) { + super(fm, ViewPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + uploadSettingsFragment = new UploadSettingsFragment(uploadInformation); + } + + @NonNull + @Override + public Fragment getItem(int position) { + switch (position) { + case 0: + return uploadSettingsFragment; + + case 1: + return uploadInfoFragment; + + default: + uploadSettingsFragment = new UploadSettingsFragment(); + return uploadSettingsFragment; + } + } + + @Nullable + @Override + public CharSequence getPageTitle(int position) { + switch (position) { + case 0: + return "Permissions"; + + case 1: + return "Credentials"; + + default: + return "N/A"; + } + } + + @Override + public int getCount() { + return 2; + } + } +} diff --git a/app/src/main/java/lu/circl/mispbump/activities/UploadInformationActivity.java b/app/src/main/java/lu/circl/mispbump/activities/UploadInformationActivity.java deleted file mode 100644 index 48e7496..0000000 --- a/app/src/main/java/lu/circl/mispbump/activities/UploadInformationActivity.java +++ /dev/null @@ -1,156 +0,0 @@ -package lu.circl.mispbump.activities; - -import android.animation.ValueAnimator; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.view.MenuItem; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.core.widget.ImageViewCompat; - -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.gson.Gson; - -import lu.circl.mispbump.R; -import lu.circl.mispbump.models.UploadInformation; - -public class UploadInformationActivity extends AppCompatActivity { - - public static String EXTRA_UPLOAD_INFO_KEY = "uploadInformation"; - - private View rootLayout; - private ImageView syncStatusIcon; - - private UploadInformation uploadInformation; - private FloatingActionButton fab; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_upload_information); - - - parseExtra(); - init(); - tintSystemBars(); - populateContent(); - } - - private void parseExtra() { - String uploadInfo = getIntent().getStringExtra(EXTRA_UPLOAD_INFO_KEY); - this.uploadInformation = new Gson().fromJson(uploadInfo, UploadInformation.class); - } - - private void init() { - rootLayout = findViewById(R.id.rootLayout); - - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - ActionBar actionBar = getSupportActionBar(); - actionBar.setHomeAsUpIndicator(R.drawable.ic_close); - actionBar.setDisplayHomeAsUpEnabled(true); - - fab = findViewById(R.id.fab); - - syncStatusIcon = findViewById(R.id.syncStatus); - } - - private void populateContent() { - switch (uploadInformation.getCurrentSyncStatus()) { - case COMPLETE: - ImageViewCompat.setImageTintList(syncStatusIcon, ColorStateList.valueOf(getColor(R.color.status_green))); - syncStatusIcon.setImageResource(R.drawable.ic_check_outline); - fab.hide(); - break; - case FAILURE: - ImageViewCompat.setImageTintList(syncStatusIcon, ColorStateList.valueOf(getColor(R.color.status_red))); - syncStatusIcon.setImageResource(R.drawable.ic_error_outline); - break; - case PENDING: - ImageViewCompat.setImageTintList(syncStatusIcon, ColorStateList.valueOf(getColor(R.color.status_amber))); - syncStatusIcon.setImageResource(R.drawable.ic_info_outline); - break; - } - - TextView name = findViewById(R.id.orgName); - name.setText(uploadInformation.getRemote().organisation.name); - } - - @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - - fab.show(); - } - - @Override - public void postponeEnterTransition() { - super.postponeEnterTransition(); - fab.show(); - } - - @Override - public void startPostponedEnterTransition() { - super.startPostponedEnterTransition(); - fab.show(); - } - - private void tintSystemBars() { - // Initial colors of each system bar. - final int statusBarColor = getColor(R.color.white); - final int toolbarColor = getColor(R.color.white); - - // Desired final colors of each bar. - final int statusBarToColor = getColor(R.color.colorPrimary); - final int toolbarToColor = getColor(R.color.colorPrimary); - - ValueAnimator anim = ValueAnimator.ofFloat(0, 1); - anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - // Use animation position to blend colors. - float position = animation.getAnimatedFraction(); - - // Apply blended color to the status bar. - int blended = blendColors(statusBarColor, statusBarToColor, position); - getWindow().setStatusBarColor(blended); - - blended = blendColors(toolbarColor, toolbarToColor, position); - getSupportActionBar().setBackgroundDrawable(new ColorDrawable(blended)); - } - }); - - anim.setDuration(500).start(); - } - - private int blendColors(int from, int to, float ratio) { - final float inverseRatio = 1f - ratio; - - final float r = Color.red(to) * ratio + Color.red(from) * inverseRatio; - final float g = Color.green(to) * ratio + Color.green(from) * inverseRatio; - final float b = Color.blue(to) * ratio + Color.blue(from) * inverseRatio; - - return Color.rgb((int) r, (int) g, (int) b); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - onBackPressed(); - return true; - } - - return super.onOptionsItemSelected(item); - } -} diff --git a/app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java b/app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java index bba9499..7dadcb7 100644 --- a/app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java +++ b/app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java @@ -5,6 +5,8 @@ import android.content.res.ColorStateList; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.core.widget.ImageViewCompat; @@ -15,34 +17,30 @@ import java.util.List; import lu.circl.mispbump.R; import lu.circl.mispbump.interfaces.OnRecyclerItemClickListener; import lu.circl.mispbump.models.UploadInformation; -import lu.circl.mispbump.viewholders.UploadInfoListViewHolder; -public class UploadInfoAdapter extends RecyclerView.Adapter { +public class UploadInfoAdapter extends RecyclerView.Adapter { private Context context; private List items; private OnRecyclerItemClickListener onRecyclerItemClickListener; + private OnRecyclerItemClickListener onRecyclerPositionClickListener; public UploadInfoAdapter(Context context) { this.context = context; } - public UploadInfoAdapter(Context context, List items) { - this.context = context; - this.items = items; - } @NonNull @Override - public UploadInfoListViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { + public UploadInfoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_upload_information, viewGroup, false); - return new UploadInfoListViewHolder(v); + return new UploadInfoAdapter.ViewHolder(v); } @Override - public void onBindViewHolder(@NonNull final UploadInfoListViewHolder holder, int position) { + public void onBindViewHolder(@NonNull final UploadInfoAdapter.ViewHolder holder, final int position) { final UploadInformation item = items.get(position); @@ -60,7 +58,7 @@ public class UploadInfoAdapter extends RecyclerView.Adapter items) { this.items = items; + notifyDataSetChanged(); } + + // callbacks + public void setOnRecyclerItemClickListener(OnRecyclerItemClickListener onRecyclerItemClickListener) { this.onRecyclerItemClickListener = onRecyclerItemClickListener; } + public void setOnRecyclerPositionClickListener(OnRecyclerItemClickListener onRecyclerPositionClickListener) { + this.onRecyclerPositionClickListener = onRecyclerPositionClickListener; + } + + // viewHolder + + static class ViewHolder extends RecyclerView.ViewHolder { + View rootView; + ImageView syncStatus; + TextView orgName, date; + + ViewHolder(@NonNull View itemView) { + super(itemView); + rootView = itemView; + orgName = itemView.findViewById(R.id.orgName); + date = itemView.findViewById(R.id.date); + syncStatus = itemView.findViewById(R.id.syncStatus); + } + } + } diff --git a/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java b/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java index 54597d0..b09f3bf 100644 --- a/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java +++ b/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java @@ -20,6 +20,40 @@ import lu.circl.mispbump.security.DiffieHellman; public class DialogManager { + public static void syncAlreadyExistsDialog(Context context, final IDialogFeedback callback) { + final AlertDialog.Builder adb = new AlertDialog.Builder(context); + + adb.setTitle("Sync information already exists"); + adb.setMessage("You already synced with this organisation, would you like to update the information?" + + "\nUpdating the information will reset the current authkey!"); + adb.setPositiveButton("Update", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (callback != null) { + callback.positive(); + } + } + }); + + adb.setNegativeButton("Exit", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (callback != null) { + callback.negative(); + } + } + }); + + Activity act = (Activity) context; + act.runOnUiThread(new Runnable() { + @Override + public void run() { + adb.create().show(); + } + }); + } + + public static void saveAndExitDialog(Context context, final IDialogFeedback callback) { final AlertDialog.Builder adb = new AlertDialog.Builder(context); diff --git a/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java b/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java index 0374059..70b4677 100644 --- a/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java +++ b/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java @@ -322,120 +322,104 @@ public class PreferenceManager { } - public void setUploadInformationList(List uploadInformationList) { - KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS); + private List cachedUploadInformationList; - try { - String cipherText = ksw.encrypt(new Gson().toJson(uploadInformationList)); - SharedPreferences.Editor editor = preferences.edit(); - editor.putString(UPLOAD_INFO, cipherText); - editor.apply(); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (BadPaddingException e) { - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - } - } - - public List getUploadInformation() { + private void loadUploadInformationList() { KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS); String storedUploadInfoString = preferences.getString(UPLOAD_INFO, null); Type type = new TypeToken>() {}.getType(); - if (storedUploadInfoString == null) { - return null; + if (storedUploadInfoString == null || storedUploadInfoString.isEmpty()) { + cachedUploadInformationList = new ArrayList<>(); + } else { + try { + storedUploadInfoString = ksw.decrypt(storedUploadInfoString); + cachedUploadInformationList = new Gson().fromJson(storedUploadInfoString, type); + } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException e) { + e.printStackTrace(); + } } + } + private void saveUploadInformationList() { try { - storedUploadInfoString = ksw.decrypt(storedUploadInfoString); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (BadPaddingException e) { - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { + KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS); + String cipherText = ksw.encrypt(new Gson().toJson(cachedUploadInformationList)); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString(UPLOAD_INFO, cipherText); + editor.apply(); + } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { e.printStackTrace(); } + } - return new Gson().fromJson(storedUploadInfoString, type); + public List getUploadInformationList() { + if (cachedUploadInformationList == null) { + loadUploadInformationList(); + } + + return cachedUploadInformationList; + } + + public void setUploadInformationList(List uploadInformationList) { + cachedUploadInformationList = uploadInformationList; + saveUploadInformationList(); + } + + public UploadInformation getUploadInformation(UUID uuid) { + if (cachedUploadInformationList == null) { + loadUploadInformationList(); + } + + for (UploadInformation ui : cachedUploadInformationList) { + if (ui.getUuid().compareTo(uuid) == 0) { + return ui; + } + } + + return null; } public void addUploadInformation(UploadInformation uploadInformation) { - List uploadInformationList = getUploadInformation(); - - if (uploadInformationList == null) { - uploadInformationList = new ArrayList<>(); - uploadInformationList.add(uploadInformation); - setUploadInformationList(uploadInformationList); - } else { - - // check if upload information already exists - for (int i = 0; i < uploadInformationList.size(); i++) { - if (uploadInformationList.get(i).getId().compareTo(uploadInformation.getId()) == 0) { - uploadInformationList.set(i, uploadInformation); - setUploadInformationList(uploadInformationList); - return; - } - } - - uploadInformationList.add(uploadInformation); - setUploadInformationList(uploadInformationList); - } - } - - public boolean containsUploadInformation(UUID uuid) { - List uploadInformationList = getUploadInformation(); - - if (uploadInformationList == null) { - return false; + if (cachedUploadInformationList == null) { + loadUploadInformationList(); } - for (UploadInformation ui : uploadInformationList) { - if (ui.getId().compareTo(uuid) == 0) { - return true; + // update if exists + for (int i = 0; i < cachedUploadInformationList.size(); i++) { + if (cachedUploadInformationList.get(i).getUuid().compareTo(uploadInformation.getUuid()) == 0) { + cachedUploadInformationList.set(i, uploadInformation); + saveUploadInformationList(); + return; } } - return false; + // else: add + cachedUploadInformationList.add(uploadInformation); + saveUploadInformationList(); } - public boolean removeUploadInformation(UUID uuid) { - Log.d("PREFS", "uuid to delete: " + uuid.toString()); + public void removeUploadInformation(UUID uuid) { + if (cachedUploadInformationList == null) { + loadUploadInformationList(); + } - List uploadInformationList = getUploadInformation(); - - for (UploadInformation ui : uploadInformationList) { - - Log.d("PREFS", "checking uuid: " + ui.getId().toString()); - - if (ui.getId().compareTo(uuid) == 0) { - if (uploadInformationList.size() == 1) { + for (UploadInformation ui : cachedUploadInformationList) { + if (ui.getUuid().compareTo(uuid) == 0) { + if (cachedUploadInformationList.size() == 1) { clearUploadInformation(); } else { - uploadInformationList.remove(ui); - setUploadInformationList(uploadInformationList); + cachedUploadInformationList.remove(ui); + saveUploadInformationList(); } - return true; } } - - - return false; } public void clearUploadInformation() { + cachedUploadInformationList.clear(); + KeyStoreWrapper keyStoreWrapper = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS); keyStoreWrapper.deleteStoredKey(); diff --git a/app/src/main/java/lu/circl/mispbump/auxiliary/QrCodeGenerator.java b/app/src/main/java/lu/circl/mispbump/auxiliary/QrCodeGenerator.java index b8a58c6..3f0b100 100644 --- a/app/src/main/java/lu/circl/mispbump/auxiliary/QrCodeGenerator.java +++ b/app/src/main/java/lu/circl/mispbump/auxiliary/QrCodeGenerator.java @@ -22,13 +22,10 @@ public class QrCodeGenerator { } public Bitmap generateQrCode(String content) { - Point displaySize = new Point(); - callingActivity.getWindowManager().getDefaultDisplay().getSize(displaySize); + int size = getDisplaySize().x; - int size = displaySize.x; - - if (displaySize.x > displaySize.y) { - size = displaySize.y; + if (size > getDisplaySize().y) { + size = getDisplaySize().y; } size = (int)(size * 0.8); @@ -48,6 +45,13 @@ public class QrCodeGenerator { return null; } + public Point getDisplaySize() { + Point displaySize = new Point(); + callingActivity.getWindowManager().getDefaultDisplay().getSize(displaySize); + + return displaySize; + } + private Bitmap createBitmap(BitMatrix matrix) { int width = matrix.getWidth(); int height = matrix.getHeight(); @@ -55,7 +59,7 @@ public class QrCodeGenerator { for (int y = 0; y < height; y++) { int offset = y * width; for (int x = 0; x < width; x++) { - pixels[offset + x] = matrix.get(x, y) ? 0xFF000000 : 0x99FFFFFF; + pixels[offset + x] = matrix.get(x, y) ? 0xFF000000 : 0x00FFFFFF; } } diff --git a/app/src/main/java/lu/circl/mispbump/customViews/ExtendedViewPager.java b/app/src/main/java/lu/circl/mispbump/customViews/ExtendedViewPager.java new file mode 100644 index 0000000..4bc6e95 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/customViews/ExtendedViewPager.java @@ -0,0 +1,32 @@ +package lu.circl.mispbump.customViews; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; + +public class ExtendedViewPager extends ViewPager { + + private boolean swipeEnabled; + + public ExtendedViewPager(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return swipeEnabled && super.onTouchEvent(event); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + return swipeEnabled && super.onTouchEvent(event); + } + + public void setPagingEnabled(boolean enabled) { + this.swipeEnabled = enabled; + } +} diff --git a/app/src/main/java/lu/circl/mispbump/customViews/MaterialPreferenceSwitch.java b/app/src/main/java/lu/circl/mispbump/customViews/MaterialPreferenceSwitch.java new file mode 100644 index 0000000..06d4ba5 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/customViews/MaterialPreferenceSwitch.java @@ -0,0 +1,63 @@ +package lu.circl.mispbump.customViews; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Switch; +import android.widget.TextView; + +import androidx.constraintlayout.widget.ConstraintLayout; + +import lu.circl.mispbump.R; + +public class MaterialPreferenceSwitch extends ConstraintLayout { + + private View rootView; + + private TextView titleView, subTitleView; + private Switch switchView; + + public MaterialPreferenceSwitch(Context context, AttributeSet attrs) { + super(context, attrs); + + View view = LayoutInflater.from(context).inflate(R.layout.material_preference_switch, this); + rootView = view.findViewById(R.id.rootLayout); + + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MaterialPreferenceSwitch); + String title = a.getString(R.styleable.MaterialPreferenceSwitch_title); + String subTitle = a.getString(R.styleable.MaterialPreferenceSwitch_subtitle); + a.recycle(); + + titleView = view.findViewById(R.id.material_preference_title); + titleView.setText(title); + + subTitleView = view.findViewById(R.id.material_preference_subtitle); + subTitleView.setText(subTitle); + + switchView = view.findViewById(R.id.material_preference_switch); + + rootView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (switchView.isEnabled()) { + switchView.setChecked(!switchView.isChecked()); + } + } + }); + } + + public void setEnabled(boolean enabled) { + switchView.setEnabled(enabled); + } + + public void setChecked(boolean checked) { + switchView.setChecked(checked); + } + + public boolean isChecked() { + return switchView.isChecked(); + } + +} diff --git a/app/src/main/java/lu/circl/mispbump/customViews/UploadAction.java b/app/src/main/java/lu/circl/mispbump/customViews/UploadAction.java index 89ef1b6..5a93377 100644 --- a/app/src/main/java/lu/circl/mispbump/customViews/UploadAction.java +++ b/app/src/main/java/lu/circl/mispbump/customViews/UploadAction.java @@ -11,11 +11,12 @@ import android.widget.ProgressBar; import android.widget.TextView; import androidx.appcompat.widget.LinearLayoutCompat; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.widget.ImageViewCompat; import lu.circl.mispbump.R; -public class UploadAction extends LinearLayoutCompat { +public class UploadAction extends ConstraintLayout { private Context context; @@ -26,7 +27,7 @@ public class UploadAction extends LinearLayoutCompat { ERROR } - private TextView errorView; + private TextView titleView, errorView; private UploadState currentUploadState; private ImageView stateView; private ProgressBar progressBar; @@ -41,20 +42,23 @@ public class UploadAction extends LinearLayoutCompat { super(context, attrs); this.context = context; + View baseView = LayoutInflater.from(context).inflate(R.layout.view_upload_action_2, this); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UploadAction); - String title = a.getString(R.styleable.UploadAction_description); + + titleView = baseView.findViewById(R.id.title); + titleView.setText(a.getString(R.styleable.UploadAction_description)); + a.recycle(); - LayoutInflater inflater = LayoutInflater.from(context); - View baseView = inflater.inflate(R.layout.view_upload_action, this); + errorView = baseView.findViewById(R.id.error); + stateView = baseView.findViewById(R.id.stateView); + progressBar = baseView.findViewById(R.id.progressBar); + } - errorView = findViewById(R.id.error); - TextView titleView = baseView.findViewById(R.id.title); + public void setTitle(String title) { titleView.setText(title); - - stateView = findViewById(R.id.stateView); - progressBar = findViewById(R.id.progressBar); } /** @@ -75,12 +79,6 @@ public class UploadAction extends LinearLayoutCompat { progressBar.setVisibility(GONE); switch (state) { - case PENDING: - stateView.setVisibility(VISIBLE); - stateView.setImageResource(R.drawable.ic_info_outline); - ImageViewCompat.setImageTintList(stateView, ColorStateList.valueOf(context.getColor(R.color.status_amber))); - break; - case LOADING: stateView.setVisibility(GONE); progressBar.setVisibility(VISIBLE); @@ -97,6 +95,12 @@ public class UploadAction extends LinearLayoutCompat { stateView.setImageResource(R.drawable.ic_error_outline); ImageViewCompat.setImageTintList(stateView, ColorStateList.valueOf(context.getColor(R.color.status_red))); break; + + case PENDING: + stateView.setVisibility(VISIBLE); + stateView.setImageResource(R.drawable.ic_info_outline); + ImageViewCompat.setImageTintList(stateView, ColorStateList.valueOf(context.getColor(R.color.status_amber))); + break; } } diff --git a/app/src/main/java/lu/circl/mispbump/fragments/CameraFragment.java b/app/src/main/java/lu/circl/mispbump/fragments/CameraFragment.java index a0f9d62..f293c4c 100644 --- a/app/src/main/java/lu/circl/mispbump/fragments/CameraFragment.java +++ b/app/src/main/java/lu/circl/mispbump/fragments/CameraFragment.java @@ -73,10 +73,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest private int lastAccessedIndex = 0; private Bitmap[] processQueue = new Bitmap[10]; - ImageProcessingThread() { - Log.i(TAG, "Image worker thread created"); - } - void addToQueue(Bitmap bitmap) { processQueue[lastAccessedIndex] = bitmap; // circular array access @@ -167,7 +163,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest @Override public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { - Log.i(TAG, "Width: " + width + "; height: " + height); openCamera(width, height); } @@ -350,7 +345,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest } else if (notBigEnough.size() > 0) { return Collections.max(notBigEnough, new CompareSizesByArea()); } else { - Log.e(TAG, "Couldn't find any suitable preview size"); return choices[0]; } } @@ -359,8 +353,7 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_camera, container, false); - hideCamView = v.findViewById(R.id.hideCam); - hideCamView.setVisibility(View.GONE); +// hideCamView = v.findViewById(R.id.hideCam); initRenderScript(); setUpBarcodeDetector(); @@ -370,6 +363,7 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest @Override public void onViewCreated(final View view, Bundle savedInstanceState) { autoFitTextureView = view.findViewById(R.id.texture); + hideCamView = view.findViewById(R.id.hideCam); } @Override @@ -381,20 +375,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest public void onResume() { super.onResume(); enablePreview(); -// startBackgroundThread(); -// -// imageProcessingThread = new ImageProcessingThread(); -// imageProcessingThread.start(); -// -// // When the screen is turned off and turned back on, the SurfaceTexture is already -// // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open -// // a camera and start preview from here (otherwise, we wait until the surface is ready in -// // the SurfaceTextureListener). -// if (autoFitTextureView.isAvailable()) { -// openCamera(autoFitTextureView.getWidth(), autoFitTextureView.getHeight()); -// } else { -// autoFitTextureView.setSurfaceTextureListener(mSurfaceTextureListener); -// } } @Override @@ -511,10 +491,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest Size[] sizes = map.getOutputSizes(SurfaceTexture.class); - for (Size size : sizes) { - Log.i(TAG, size.toString()); - } - // Danger, W.R.! Attempting to use too large a preview size could exceed the camera // bus' bandwidth limitation, resulting in gorgeous previews but the storage of // garbage capture data. @@ -802,12 +778,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest public interface QrScanCallback { void qrScanResult(String qrData); } - - public interface CameraReadyCallback { - void ready(); - } - - private CameraReadyCallback cameraReadyCallback; private boolean readQrEnabled = true; private BarcodeDetector barcodeDetector; private RenderScript renderScript; @@ -865,6 +835,18 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest } public void disablePreview() { + + if (hideCamView.getAlpha() == 1 && hideCamView.getVisibility() == View.VISIBLE) { + closeCamera(); + stopBackgroundThread(); + + if (imageProcessingThread.isAlive()) { + imageProcessingThread.isRunning = false; + } + + return; + } + hideCamView.setAlpha(0f); hideCamView.setVisibility(View.VISIBLE); hideCamView.animate() @@ -896,19 +878,15 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest autoFitTextureView.setSurfaceTextureListener(mSurfaceTextureListener); } - hideCamView.setAlpha(1f); hideCamView.setVisibility(View.VISIBLE); + hideCamView.setAlpha(1f); hideCamView.animate() .alpha(0f) - .setStartDelay(100) .setDuration(1000) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { hideCamView.setVisibility(View.GONE); - if (cameraReadyCallback != null) { - cameraReadyCallback.ready(); - } } }); } @@ -926,9 +904,5 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest public void setOnQrAvailableListener(QrScanCallback callback) { qrResultCallback = callback; } - - public void setCameraReadyCallback(CameraReadyCallback callback) { - this.cameraReadyCallback = callback; - } } diff --git a/app/src/main/java/lu/circl/mispbump/fragments/SyncFragmentAdapter.java b/app/src/main/java/lu/circl/mispbump/fragments/SyncFragmentAdapter.java new file mode 100644 index 0000000..bb59154 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/fragments/SyncFragmentAdapter.java @@ -0,0 +1,75 @@ +package lu.circl.mispbump.fragments; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; + +public class SyncFragmentAdapter extends FragmentPagerAdapter { + + public CameraFragment cameraFragment_1, cameraFragment_2; + private UploadSettingsFragment uploadSettingsFragment; + + public SyncFragmentAdapter(@NonNull FragmentManager fm) { + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + private CameraFragment.QrScanCallback scanCallback; + + @NonNull + @Override + public Fragment getItem(int position) { + switch (position) { + case 0: + if (cameraFragment_1 == null) { + cameraFragment_1 = new CameraFragment(); + } + + if (scanCallback != null) { + cameraFragment_1.setOnQrAvailableListener(scanCallback); + } + + return cameraFragment_1; + + case 1: + if (cameraFragment_2 == null) { + cameraFragment_2 = new CameraFragment(); + } + + if (scanCallback != null) { + cameraFragment_1.setOnQrAvailableListener(scanCallback); + } + + return cameraFragment_2; + + case 2: + if (uploadSettingsFragment == null) { + uploadSettingsFragment = new UploadSettingsFragment(); + } + + return uploadSettingsFragment; + + default: + return new CameraFragment(); + } + } + + public void setQrReceivedCallback(CameraFragment.QrScanCallback qrScanCallback) { + this.scanCallback = qrScanCallback; + } + + public void disableCameraPreview() { + if (cameraFragment_1 != null) { + cameraFragment_1.disablePreview(); + } + + if (cameraFragment_2 != null) { + cameraFragment_2.disablePreview(); + } + } + + @Override + public int getCount() { + return 3; + } +} diff --git a/app/src/main/java/lu/circl/mispbump/fragments/UploadInfoFragment.java b/app/src/main/java/lu/circl/mispbump/fragments/UploadInfoFragment.java new file mode 100644 index 0000000..cea77c9 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/fragments/UploadInfoFragment.java @@ -0,0 +1,24 @@ +package lu.circl.mispbump.fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import lu.circl.mispbump.R; + +public class UploadInfoFragment extends Fragment { + + public UploadInfoFragment () {} + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_upload_info, container, false); + + return v; + } + +} diff --git a/app/src/main/java/lu/circl/mispbump/fragments/SyncOptionsFragment.java b/app/src/main/java/lu/circl/mispbump/fragments/UploadSettingsFragment.java similarity index 54% rename from app/src/main/java/lu/circl/mispbump/fragments/SyncOptionsFragment.java rename to app/src/main/java/lu/circl/mispbump/fragments/UploadSettingsFragment.java index 81f289d..09a92fe 100644 --- a/app/src/main/java/lu/circl/mispbump/fragments/SyncOptionsFragment.java +++ b/app/src/main/java/lu/circl/mispbump/fragments/UploadSettingsFragment.java @@ -1,36 +1,63 @@ package lu.circl.mispbump.fragments; import android.os.Bundle; -import androidx.annotation.NonNull; - -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Switch; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import lu.circl.mispbump.R; +import lu.circl.mispbump.customViews.MaterialPreferenceSwitch; +import lu.circl.mispbump.models.UploadInformation; -public class SyncOptionsFragment extends Fragment { +public class UploadSettingsFragment extends Fragment { - private Switch allowSelfSigned, push, pull, cache; + private MaterialPreferenceSwitch allowSelfSigned, push, pull, cache; + private UploadInformation uploadInformation; + + public UploadSettingsFragment() { + } + + public UploadSettingsFragment(UploadInformation uploadInformation) { + this.uploadInformation = uploadInformation; + } @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_sync_options, container, false); + View v = inflater.inflate(R.layout.fragment_upload_settings, container, false); allowSelfSigned = v.findViewById(R.id.self_signed_switch); push = v.findViewById(R.id.push_switch); pull = v.findViewById(R.id.pull_switch); cache = v.findViewById(R.id.cache_switch); + populateContent(); + return v; } + private void populateContent() { + if (uploadInformation == null) { + return; + } + + allowSelfSigned.setChecked(uploadInformation.isAllowSelfSigned()); + push.setChecked(uploadInformation.isPush()); + pull.setChecked(uploadInformation.isPull()); + cache.setChecked(uploadInformation.isCached()); + } + + public void setUploadInfo(UploadInformation uploadInfo) { + this.uploadInformation = uploadInfo; + } + public boolean getAllowSelfSigned() { return allowSelfSigned.isChecked(); } + public void setAllowSelfSigned(boolean allowSelfSigned) { this.allowSelfSigned.setChecked(allowSelfSigned); } @@ -38,6 +65,7 @@ public class SyncOptionsFragment extends Fragment { public boolean getPush() { return push.isChecked(); } + public void setPush(boolean push) { this.push.setChecked(push); } @@ -45,6 +73,7 @@ public class SyncOptionsFragment extends Fragment { public boolean getPull() { return pull.isChecked(); } + public void setPull(boolean pull) { this.pull.setChecked(pull); } @@ -52,6 +81,7 @@ public class SyncOptionsFragment extends Fragment { public boolean getCache() { return cache.isChecked(); } + public void setCache(boolean cache) { this.cache.setChecked(cache); } diff --git a/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java b/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java index 8c4cc59..036bbb2 100644 --- a/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java +++ b/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java @@ -2,14 +2,13 @@ package lu.circl.mispbump.models; import androidx.annotation.NonNull; -import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.UUID; -public class UploadInformation implements Serializable { +public class UploadInformation { public enum SyncStatus { COMPLETE, @@ -17,7 +16,7 @@ public class UploadInformation implements Serializable { PENDING } - private UUID id; + private UUID uuid; private SyncStatus currentSyncStatus = SyncStatus.PENDING; @@ -29,19 +28,8 @@ public class UploadInformation implements Serializable { private Date date; public UploadInformation() { - this(null, null); - } - - public UploadInformation(SyncInformation local) { - this(local, null); - } - - public UploadInformation(SyncInformation local, SyncInformation remote) { - id = UUID.randomUUID(); + uuid = UUID.randomUUID(); date = Calendar.getInstance().getTime(); - - this.local = local; - this.remote = remote; } // getter and setter @@ -67,11 +55,11 @@ public class UploadInformation implements Serializable { return remote; } - public UUID getId() { - return id; + public UUID getUuid() { + return uuid; } - public void setId(UUID id) { - this.id = id; + public void setUuid(UUID uuid) { + this.uuid = uuid; } public void setDate() { diff --git a/app/src/main/java/lu/circl/mispbump/viewholders/UploadInfoListViewHolder.java b/app/src/main/java/lu/circl/mispbump/viewholders/UploadInfoListViewHolder.java deleted file mode 100644 index ce2b890..0000000 --- a/app/src/main/java/lu/circl/mispbump/viewholders/UploadInfoListViewHolder.java +++ /dev/null @@ -1,27 +0,0 @@ -package lu.circl.mispbump.viewholders; - -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import lu.circl.mispbump.R; - -public class UploadInfoListViewHolder extends RecyclerView.ViewHolder { - - public View rootView; - public ImageView syncStatus; - public TextView orgName, date; - - public UploadInfoListViewHolder(@NonNull View itemView) { - super(itemView); - - rootView = itemView; - - orgName = itemView.findViewById(R.id.orgName); - date = itemView.findViewById(R.id.date); - syncStatus = itemView.findViewById(R.id.syncStatus); - } -} diff --git a/app/src/main/res/drawable/ic_autorenew.xml b/app/src/main/res/drawable/ic_autorenew.xml index 3bdee48..62aa67a 100644 --- a/app/src/main/res/drawable/ic_autorenew.xml +++ b/app/src/main/res/drawable/ic_autorenew.xml @@ -1,5 +1,9 @@ - - + + diff --git a/app/src/main/res/drawable/ic_error_outline.xml b/app/src/main/res/drawable/ic_error_outline.xml index 694bd6e..622ae00 100644 --- a/app/src/main/res/drawable/ic_error_outline.xml +++ b/app/src/main/res/drawable/ic_error_outline.xml @@ -1,5 +1,9 @@ - - + + diff --git a/app/src/main/res/drawable/ic_pending.xml b/app/src/main/res/drawable/ic_pending.xml new file mode 100644 index 0000000..b7695ae --- /dev/null +++ b/app/src/main/res/drawable/ic_pending.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/rect_rounded.xml b/app/src/main/res/drawable/rect_rounded.xml new file mode 100644 index 0000000..fc92cfa --- /dev/null +++ b/app/src/main/res/drawable/rect_rounded.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_rect.xml b/app/src/main/res/drawable/rect_rounded_top.xml similarity index 66% rename from app/src/main/res/drawable/rounded_rect.xml rename to app/src/main/res/drawable/rect_rounded_top.xml index 8daefbc..7490f72 100644 --- a/app/src/main/res/drawable/rounded_rect.xml +++ b/app/src/main/res/drawable/rect_rounded_top.xml @@ -4,6 +4,6 @@ android:color="@color/white"/> + android:topLeftRadius="12dp" + android:topRightRadius="12dp"/> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 5efa0f8..07e44a6 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -4,8 +4,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/rootLayout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:animateLayoutChanges="true"> + android:layout_height="match_parent"> + android:theme="@style/ToolbarTheme" + app:popupTheme="@style/PopupTheme" /> + android:layout_height="match_parent" /> + app:layout_constraintTop_toBottomOf="@id/toolbar" /> - - + android:layout_height="match_parent"/> - - - - - - - - + android:layout_gravity="center" + android:background="@drawable/rect_rounded" + android:backgroundTint="#99FFFFFF" + android:padding="8dp" + tools:layout_height="256dp" + tools:layout_width="256dp"> - - - - - - - - - - + android:orientation="vertical" + android:gravity="center_vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_upload.xml b/app/src/main/res/layout/activity_upload.xml index 37d81d9..ad95b5c 100644 --- a/app/src/main/res/layout/activity_upload.xml +++ b/app/src/main/res/layout/activity_upload.xml @@ -34,10 +34,10 @@ android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" - app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:title="Check if instance is available" /> + app:layout_constraintEnd_toEndOf="parent" + app:description="Check if instance is available" /> + app:layout_constraintEnd_toEndOf="parent" + app:description="Add organisation" /> + app:description="Add sync user" /> + app:description="Add sync server" /> - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_upload_information.xml b/app/src/main/res/layout/activity_upload_information.xml deleted file mode 100644 index 017a195..0000000 --- a/app/src/main/res/layout/activity_upload_information.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_upload_information_2.xml b/app/src/main/res/layout/activity_upload_information_2.xml new file mode 100644 index 0000000..0e67559 --- /dev/null +++ b/app/src/main/res/layout/activity_upload_information_2.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/custom_upload_info_title.xml b/app/src/main/res/layout/custom_upload_info_title.xml new file mode 100644 index 0000000..c65743f --- /dev/null +++ b/app/src/main/res/layout/custom_upload_info_title.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_sync_options.xml b/app/src/main/res/layout/fragment_sync_options.xml deleted file mode 100644 index 7b32505..0000000 --- a/app/src/main/res/layout/fragment_sync_options.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_upload_info.xml b/app/src/main/res/layout/fragment_upload_info.xml new file mode 100644 index 0000000..e387c92 --- /dev/null +++ b/app/src/main/res/layout/fragment_upload_info.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_upload_settings.xml b/app/src/main/res/layout/fragment_upload_settings.xml new file mode 100644 index 0000000..3f525e9 --- /dev/null +++ b/app/src/main/res/layout/fragment_upload_settings.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/material_preference_switch.xml b/app/src/main/res/layout/material_preference_switch.xml new file mode 100644 index 0000000..c302a9c --- /dev/null +++ b/app/src/main/res/layout/material_preference_switch.xml @@ -0,0 +1,58 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_upload_action.xml b/app/src/main/res/layout/view_upload_action.xml index 9d88e7c..8ba6929 100644 --- a/app/src/main/res/layout/view_upload_action.xml +++ b/app/src/main/res/layout/view_upload_action.xml @@ -13,11 +13,13 @@ + android:layout_height="wrap_content" + android:layout_weight="1" + android:textAppearance="@style/Text.Title" + android:layout_gravity="center_vertical" + android:layout_marginEnd="8dp" /> + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_sync.xml b/app/src/main/res/menu/menu_upload_info.xml similarity index 67% rename from app/src/main/res/menu/menu_sync.xml rename to app/src/main/res/menu/menu_upload_info.xml index 7d4e56e..5ba86a6 100644 --- a/app/src/main/res/menu/menu_sync.xml +++ b/app/src/main/res/menu/menu_upload_info.xml @@ -2,8 +2,8 @@ + android:title="Remove" + app:showAsAction="never"/> \ No newline at end of file diff --git a/app/src/main/res/transition/simple.xml b/app/src/main/res/transition/simple.xml deleted file mode 100644 index f903d98..0000000 --- a/app/src/main/res/transition/simple.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 0f1622a..a0817fb 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -1,9 +1,18 @@ + + + + - - + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d7be4be..949f632 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,17 +2,17 @@ #047EB4 #023850 - #12B3FA - #8012B3FA #33000000 - #FFFFFF #80FFFFFF #BDBDBD + #F0F0F0 + + #000 #4CAF50 #FB8C00 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7738241..f614644 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,7 +6,6 @@ MISP Automation Key No Information Save Automation Key - Home Help MISP Server URL\nPublic MISP URL\n\nMISP Automation key\nZu finden unter ... Okay diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 45a61b0..492b408 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,21 +1,8 @@ - - - +