diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..681f41a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 3e34bb9..f7bd235 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,11 +3,11 @@ apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { applicationId "lu.circl.mispbump" minSdkVersion 23 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -26,6 +26,10 @@ android { buildToolsVersion = '29.0.1' } +repositories { + mavenCentral() +} + dependencies { // android implementation 'com.google.android.material:material:1.0.0' @@ -37,9 +41,9 @@ dependencies { implementation 'androidx.preference:preference:1.1.0-rc01' // retrofit - implementation 'com.squareup.retrofit2:retrofit:2.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.5.0' - implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' + implementation 'com.squareup.retrofit2:retrofit:2.6.1' + implementation 'com.squareup.retrofit2:converter-gson:2.6.1' + implementation 'com.squareup.okhttp3:logging-interceptor:4.1.0' // barcode reading implementation 'com.google.android.gms:play-services-vision:18.0.0' @@ -53,7 +57,5 @@ dependencies { androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} -repositories { - mavenCentral() + implementation project(path: ':expandablecardview') } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index abd7c7c..dd0814a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,12 +1,8 @@ - - - - - + + - + - + + android:label="@string/login" /> + android:label="@string/app_name" /> + android:theme="@style/AppTheme.Translucent" /> - + android:label="@string/sync_details_activity_label" + android:parentActivityName=".activities.HomeActivity" /> + android:parentActivityName=".activities.HomeActivity" /> + android:theme="@style/AppTheme.Translucent" /> + + + diff --git a/app/src/main/java/lu/circl/mispbump/activities/ExchangeActivity.java b/app/src/main/java/lu/circl/mispbump/activities/ExchangeActivity.java index 978c186..7049e10 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/ExchangeActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/ExchangeActivity.java @@ -22,16 +22,14 @@ import com.google.gson.JsonSyntaxException; import java.security.NoSuchAlgorithmException; 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.models.ExchangeInformation; import lu.circl.mispbump.models.SyncInformation; -import lu.circl.mispbump.models.UploadInformation; +import lu.circl.mispbump.models.restModels.Server; import lu.circl.mispbump.security.DiffieHellman; @@ -40,7 +38,6 @@ public class ExchangeActivity extends AppCompatActivity { private PreferenceManager preferenceManager; private QrCodeGenerator qrCodeGenerator; private DiffieHellman diffieHellman; - private UploadInformation uploadInformation; private CameraFragment cameraFragment; @@ -50,6 +47,8 @@ public class ExchangeActivity extends AppCompatActivity { private ImageView qrCode; private ImageButton prevButton, nextButton; + private SyncInformation syncInformation; + private Bitmap publicKeyQr, dataQr; private SyncState currentSyncState; @@ -67,9 +66,11 @@ public class ExchangeActivity extends AppCompatActivity { initViews(); initCamera(); - uploadInformation = new UploadInformation(); publicKeyQr = generatePublicKeyBitmap(); + syncInformation = new SyncInformation(); + syncInformation.setLocal(generateSyncExchangeInformation()); + setSyncState(SyncState.KEY_EXCHANGE); } @@ -105,95 +106,86 @@ public class ExchangeActivity extends AppCompatActivity { fragmentTransaction.commit(); } + private ExchangeInformation generateSyncExchangeInformation() { + ExchangeInformation exchangeInformation = new ExchangeInformation(); + exchangeInformation.setOrganisation(preferenceManager.getUserOrganisation().toSyncOrganisation()); + exchangeInformation.setSyncUser(preferenceManager.getUserInfo().toSyncUser()); + exchangeInformation.setServer(new Server(preferenceManager.getUserCredentials().first)); + return exchangeInformation; + } + private Bitmap generatePublicKeyBitmap() { return qrCodeGenerator.generateQrCode(DiffieHellman.publicKeyToString(diffieHellman.getPublicKey())); } private Bitmap generateLocalSyncInfoBitmap() { - uploadInformation.setLocal(generateLocalSyncInfo()); - return qrCodeGenerator.generateQrCode(diffieHellman.encrypt(new Gson().toJson(uploadInformation.getLocal()))); - } - - private SyncInformation generateLocalSyncInfo() { - SyncInformation syncInformation = new SyncInformation(); - syncInformation.organisation = preferenceManager.getUserOrganisation().toSyncOrganisation(); - syncInformation.syncUserAuthkey = new RandomString(40).nextString(); - syncInformation.baseUrl = preferenceManager.getUserCredentials().first; - syncInformation.syncUserPassword = new RandomString(16).nextString(); - syncInformation.syncUserEmail = preferenceManager.getUserInfo().email; - return syncInformation; + return qrCodeGenerator.generateQrCode(diffieHellman.encrypt(new Gson().toJson(syncInformation.getLocal()))); } private void showQrCode(final Bitmap bitmap) { - runOnUiThread(new Runnable() { - @Override - public void run() { - qrCode.setImageBitmap(bitmap); - qrFrame.setVisibility(View.VISIBLE); - } + runOnUiThread(() -> { + qrCode.setImageBitmap(bitmap); + qrFrame.setVisibility(View.VISIBLE); }); } private void setSyncState(SyncState state) { currentSyncState = state; - runOnUiThread(new Runnable() { - @Override - public void run() { - switch (currentSyncState) { - case KEY_EXCHANGE: - prevButton.setImageDrawable(getDrawable(R.drawable.ic_close)); - prevButton.setVisibility(View.VISIBLE); - nextButton.setVisibility(View.GONE); + runOnUiThread(() -> { + switch (currentSyncState) { + case KEY_EXCHANGE: + prevButton.setImageDrawable(getDrawable(R.drawable.ic_close)); + prevButton.setVisibility(View.VISIBLE); + nextButton.setVisibility(View.GONE); - setCameraPreviewEnabled(true); - showQrCode(publicKeyQr); + setCameraPreviewEnabled(true); + showQrCode(publicKeyQr); - setReadQrStatus(ReadQrStatus.PENDING); - scanFeedbackText.setText(R.string.scan_qr_hint); - qrContentInfo.setText(R.string.public_key); - break; - case KEY_EXCHANGE_DONE: - prevButton.setImageDrawable(getDrawable(R.drawable.ic_close)); - prevButton.setVisibility(View.VISIBLE); - nextButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_forward)); - nextButton.setVisibility(View.VISIBLE); + setReadQrStatus(ReadQrStatus.PENDING); + scanFeedbackText.setText(R.string.scan_qr_hint); + qrContentInfo.setText(R.string.public_key); + break; + case KEY_EXCHANGE_DONE: + prevButton.setImageDrawable(getDrawable(R.drawable.ic_close)); + prevButton.setVisibility(View.VISIBLE); + nextButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_forward)); + nextButton.setVisibility(View.VISIBLE); - setCameraPreviewEnabled(false); - showQrCode(publicKeyQr); + setCameraPreviewEnabled(false); + showQrCode(publicKeyQr); - setReadQrStatus(ReadQrStatus.SUCCESS); - scanFeedbackText.setText(R.string.public_key_received_hint); - qrContentInfo.setText(R.string.public_key); - break; - case DATA_EXCHANGE: - prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back)); - prevButton.setVisibility(View.VISIBLE); - nextButton.setVisibility(View.GONE); + setReadQrStatus(ReadQrStatus.SUCCESS); + scanFeedbackText.setText(R.string.public_key_received_hint); + qrContentInfo.setText(R.string.public_key); + break; + case DATA_EXCHANGE: + prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back)); + prevButton.setVisibility(View.VISIBLE); + nextButton.setVisibility(View.GONE); - setCameraPreviewEnabled(true); - showQrCode(dataQr); + setCameraPreviewEnabled(true); + showQrCode(dataQr); - setReadQrStatus(ReadQrStatus.PENDING); - scanFeedbackText.setText(R.string.scan_qr_hint); - qrContentInfo.setText(R.string.sync_information); - break; - case DATA_EXCHANGE_DONE: - prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back)); - prevButton.setVisibility(View.VISIBLE); - nextButton.setImageDrawable(getDrawable(R.drawable.ic_check)); - nextButton.setVisibility(View.VISIBLE); + setReadQrStatus(ReadQrStatus.PENDING); + scanFeedbackText.setText(R.string.scan_qr_hint); + qrContentInfo.setText(R.string.sync_information); + break; + case DATA_EXCHANGE_DONE: + prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back)); + prevButton.setVisibility(View.VISIBLE); + nextButton.setImageDrawable(getDrawable(R.drawable.ic_check)); + nextButton.setVisibility(View.VISIBLE); - setCameraPreviewEnabled(false); - showQrCode(dataQr); + setCameraPreviewEnabled(false); + showQrCode(dataQr); - setReadQrStatus(ReadQrStatus.SUCCESS); - scanFeedbackText.setText(R.string.sync_info_received_hint); - qrContentInfo.setText(R.string.public_key); - break; - } + setReadQrStatus(ReadQrStatus.SUCCESS); + scanFeedbackText.setText(R.string.sync_info_received_hint); + qrContentInfo.setText(R.string.public_key); + break; } }); } @@ -261,107 +253,70 @@ public class ExchangeActivity extends AppCompatActivity { private CameraFragment.QrScanCallback onQrScanned() { - return new CameraFragment.QrScanCallback() { - @Override - public void qrScanResult(String qrData) { - cameraFragment.setReadQrEnabled(false); + return qrData -> { + cameraFragment.setReadQrEnabled(false); - switch (currentSyncState) { - case KEY_EXCHANGE: - try { - diffieHellman.setForeignPublicKey(DiffieHellman.publicKeyFromString(qrData)); - setSyncState(SyncState.KEY_EXCHANGE_DONE); - dataQr = generateLocalSyncInfoBitmap(); - } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { - if (currentReadQrStatus == ReadQrStatus.PENDING) { - setReadQrStatus(ReadQrStatus.FAILURE); - Snackbar.make(rootLayout, "Public key not parsable", Snackbar.LENGTH_LONG).show(); - } - - cameraFragment.setReadQrEnabled(true); + switch (currentSyncState) { + case KEY_EXCHANGE: + try { + diffieHellman.setForeignPublicKey(DiffieHellman.publicKeyFromString(qrData)); + setSyncState(SyncState.KEY_EXCHANGE_DONE); + dataQr = generateLocalSyncInfoBitmap(); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + if (currentReadQrStatus == ReadQrStatus.PENDING) { + setReadQrStatus(ReadQrStatus.FAILURE); + Snackbar.make(rootLayout, "Public key not parsable", Snackbar.LENGTH_LONG).show(); } - break; - case DATA_EXCHANGE: - try { - final SyncInformation remoteSyncInfo = new Gson().fromJson(diffieHellman.decrypt(qrData), SyncInformation.class); - final List uploadInformationList = preferenceManager.getUploadInformationList(); - - for (final UploadInformation ui : uploadInformationList) { - if (ui.getRemote().organisation.getUuid().equals(remoteSyncInfo.organisation.getUuid())) { - DialogManager.syncAlreadyExistsDialog(ui.getRemote(), remoteSyncInfo, ExchangeActivity.this, new DialogManager.IDialogFeedback() { - @Override - public void positive() { - // update remote info only - uploadInformation.setUuid(ui.getUuid()); - uploadInformation.setDate(); - } - - @Override - public void negative() { - // replace credentials too - uploadInformationList.remove(ui); - preferenceManager.setUploadInformationList(uploadInformationList); - } - }); - - break; - } - } - - uploadInformation.setRemote(remoteSyncInfo); - preferenceManager.addUploadInformation(uploadInformation); - setSyncState(SyncState.DATA_EXCHANGE_DONE); - } catch (JsonSyntaxException e) { - if (currentReadQrStatus == ReadQrStatus.PENDING) { - setReadQrStatus(ReadQrStatus.FAILURE); - Snackbar.make(rootLayout, "Sync information not parsable", Snackbar.LENGTH_LONG).show(); - } - - cameraFragment.setReadQrEnabled(true); + cameraFragment.setReadQrEnabled(true); + } + break; + case DATA_EXCHANGE: + try { + syncInformation.setRemote(new Gson().fromJson(diffieHellman.decrypt(qrData), ExchangeInformation.class)); + preferenceManager.addSyncInformation(syncInformation); + setSyncState(SyncState.DATA_EXCHANGE_DONE); + } catch (JsonSyntaxException e) { + if (currentReadQrStatus == ReadQrStatus.PENDING) { + setReadQrStatus(ReadQrStatus.FAILURE); + Snackbar.make(rootLayout, "Sync information not parsable", Snackbar.LENGTH_LONG).show(); } - break; - } + + cameraFragment.setReadQrEnabled(true); + } + break; } }; } private View.OnClickListener onPrevClicked() { - return new View.OnClickListener() { - @Override - public void onClick(View v) { - switch (currentSyncState) { - case KEY_EXCHANGE: - case KEY_EXCHANGE_DONE: - // TODO warning that sync will be lost - finish(); - break; - case DATA_EXCHANGE: - case DATA_EXCHANGE_DONE: - setSyncState(SyncState.KEY_EXCHANGE_DONE); - break; - } + return v -> { + switch (currentSyncState) { + case KEY_EXCHANGE: + case KEY_EXCHANGE_DONE: + // TODO warning that sync will be lost + finish(); + break; + case DATA_EXCHANGE: + case DATA_EXCHANGE_DONE: + setSyncState(SyncState.KEY_EXCHANGE_DONE); + break; } }; } private View.OnClickListener onNextClicked() { - return new View.OnClickListener() { - @Override - public void onClick(View v) { - switch (currentSyncState) { - case KEY_EXCHANGE_DONE: - setSyncState(SyncState.DATA_EXCHANGE); - break; - case DATA_EXCHANGE_DONE: - uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.PENDING); - preferenceManager.addUploadInformation(uploadInformation); - Intent i = new Intent(ExchangeActivity.this, UploadInfoActivity.class); - i.putExtra(UploadInfoActivity.EXTRA_UPLOAD_INFO_UUID, uploadInformation.getUuid()); - startActivity(i); - finish(); - break; - } + return v -> { + switch (currentSyncState) { + case KEY_EXCHANGE_DONE: + setSyncState(SyncState.DATA_EXCHANGE); + break; + case DATA_EXCHANGE_DONE: + Intent i = new Intent(ExchangeActivity.this, SyncInfoDetailActivity.class); + i.putExtra(SyncInfoDetailActivity.EXTRA_SYNC_INFO_UUID, syncInformation.getUuid()); + startActivity(i); + finish(); + break; } }; } 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 b3bf483..e714148 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/HomeActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/HomeActivity.java @@ -11,37 +11,56 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityOptionsCompat; +import androidx.core.util.Pair; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; import java.util.List; import lu.circl.mispbump.R; -import lu.circl.mispbump.adapters.UploadInfoAdapter; +import lu.circl.mispbump.adapters.SyncInfoAdapter; +import lu.circl.mispbump.auxiliary.MispRestClient; import lu.circl.mispbump.auxiliary.PreferenceManager; import lu.circl.mispbump.interfaces.OnRecyclerItemClickListener; -import lu.circl.mispbump.models.UploadInformation; +import lu.circl.mispbump.models.ExchangeInformation; +import lu.circl.mispbump.models.SyncInformation; +import lu.circl.mispbump.models.restModels.MispServer; +import lu.circl.mispbump.models.restModels.MispUser; +import lu.circl.mispbump.models.restModels.Organisation; +import lu.circl.mispbump.models.restModels.Role; +import lu.circl.mispbump.models.restModels.Server; +import lu.circl.mispbump.models.restModels.User; public class HomeActivity extends AppCompatActivity { - private List uploadInformationList; + private List syncInformationList; private PreferenceManager preferenceManager; + private MispRestClient restClient; + private RecyclerView recyclerView; - private UploadInfoAdapter uploadInfoAdapter; + private SyncInfoAdapter syncInfoAdapter; private TextView emptyRecyclerView; + private SwipeRefreshLayout swipeRefreshLayout; + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); preferenceManager = PreferenceManager.getInstance(this); + Pair credentials = preferenceManager.getUserCredentials(); + restClient = MispRestClient.getInstance(credentials.first, credentials.second); initViews(); initRecyclerView(); + checkRequiredInformationAvailable(); } @Override @@ -62,7 +81,6 @@ public class HomeActivity extends AppCompatActivity { return true; } - // invoke superclass to handle unrecognized item (eg. homeAsUp) return super.onOptionsItemSelected(item); } @@ -80,46 +98,187 @@ public class HomeActivity extends AppCompatActivity { 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, ExchangeActivity.class)); - } + syncFab.setOnClickListener(v -> startActivity(new Intent(HomeActivity.this, ExchangeActivity.class))); + + swipeRefreshLayout = findViewById(R.id.swipeRefresh); + swipeRefreshLayout.setOnRefreshListener(() -> { + checkUnimportedSyncs(); + + syncInfoAdapter.setItems(syncInformationList); }); } 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); + syncInfoAdapter = new SyncInfoAdapter(); + syncInfoAdapter.setOnRecyclerPositionClickListener(onRecyclerItemClickListener()); + recyclerView.setAdapter(syncInfoAdapter); } private void refreshRecyclerView() { - uploadInformationList = preferenceManager.getUploadInformationList(); + syncInformationList = preferenceManager.getSyncInformationList(); - if (uploadInformationList.isEmpty()) { + if (syncInformationList.isEmpty()) { emptyRecyclerView.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); } else { emptyRecyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); - uploadInfoAdapter.setItems(uploadInformationList); + syncInfoAdapter.setItems(syncInformationList); } } + private void checkRequiredInformationAvailable() { + if (preferenceManager.getRoles() == null || preferenceManager.getUserInfo() == null || preferenceManager.getUserOrganisation() == null) { + + Pair credentials = preferenceManager.getUserCredentials(); + MispRestClient client = MispRestClient.getInstance(credentials.first, credentials.second); + + // get roles + client.getRoles(new MispRestClient.AllRolesCallback() { + @Override + public void success(Role[] roles) { + preferenceManager.setRoles(roles); + } + + @Override + public void failure(String error) { + Snackbar.make(recyclerView, error, Snackbar.LENGTH_LONG).show(); + } + }); + + // get user and organisation + client.getMyUser(new MispRestClient.UserCallback() { + @Override + public void success(User user) { + preferenceManager.setMyUser(user); + + client.getOrganisation(user.getOrgId(), new MispRestClient.OrganisationCallback() { + @Override + public void success(Organisation organisation) { + preferenceManager.setMyOrganisation(organisation); + } + @Override + public void failure(String error) { + Snackbar.make(recyclerView, error, Snackbar.LENGTH_LONG).show(); + } + }); + } + + @Override + public void failure(String error) { + Snackbar.make(recyclerView, error, Snackbar.LENGTH_LONG).show(); + } + }); + } + } + + private void checkUnimportedSyncs() { + restClient.getAllServers(new MispRestClient.AllRawServersCallback() { + @Override + public void success(List mispServers) { + if (mispServers.size() < 1) { + return; + } + + List syncInformationList = preferenceManager.getSyncInformationList(); + + for (MispServer mispServer : mispServers) { + + boolean existsOffline = false; + + for (SyncInformation syncInformation : syncInformationList) { + int localServerId = syncInformation.getRemote().getServer().getId(); + int remoteServerId = mispServer.getServer().getId(); + + if (remoteServerId == localServerId) { + existsOffline = true; + break; + } + } + + if (!existsOffline) { + // mispServer is not locally available + SyncInformation syncInformation = new SyncInformation(); + + ExchangeInformation local = new ExchangeInformation(); + local.setOrganisation(preferenceManager.getUserOrganisation().toSyncOrganisation()); + User syncUser = preferenceManager.getUserInfo().toSyncUser(); + syncUser.setAuthkey("Could not be recovered"); + syncUser.setPassword("Could not be recovered"); + local.setSyncUser(syncUser); + local.setServer(new Server(preferenceManager.getUserCredentials().first)); + + ExchangeInformation remote = new ExchangeInformation(); + remote.setServer(mispServer.getServer()); + + restClient.getOrganisation(mispServer.getRemoteOrganisation().getId(), new MispRestClient.OrganisationCallback() { + @Override + public void success(Organisation organisation) { + remote.setOrganisation(organisation); + + restClient.getAllUsers(new MispRestClient.AllMispUsersCallback() { + @Override + public void success(List users) { + for (MispUser mispUser : users) { + + boolean isSyncUserRole = false; + + Role[] roles = preferenceManager.getRoles(); + + for (Role role : roles) { + if (role.getId().equals(mispUser.getRole().getId())) { + isSyncUserRole = role.isSyncUserRole(); + break; + } + } + + if (mispUser.getOrganisation().getId().equals(organisation.getId()) && isSyncUserRole) { + remote.setSyncUser(mispUser.getUser()); + + syncInformation.setLocal(local); + syncInformation.setRemote(remote); + + preferenceManager.addSyncInformation(syncInformation); + refreshRecyclerView(); + } + } + } + @Override + public void failure(String error) { + swipeRefreshLayout.setRefreshing(false); + Snackbar.make(recyclerView, error, Snackbar.LENGTH_LONG).show(); + } + }); + } + + @Override + public void failure(String error) { + swipeRefreshLayout.setRefreshing(false); + Snackbar.make(recyclerView, error, Snackbar.LENGTH_LONG).show(); + } + }); + } + } + + swipeRefreshLayout.setRefreshing(false); + } + + @Override + public void failure(String error) { + swipeRefreshLayout.setRefreshing(false); + } + }); + } 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()); + return (v, index) -> { + Intent i = new Intent(HomeActivity.this, SyncInfoDetailActivity.class); + i.putExtra(SyncInfoDetailActivity.EXTRA_SYNC_INFO_UUID, syncInformationList.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()); - } + 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/LauncherActivity.java similarity index 94% rename from app/src/main/java/lu/circl/mispbump/activities/StartUpActivity.java rename to app/src/main/java/lu/circl/mispbump/activities/LauncherActivity.java index 2f29d5b..e5e18f3 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/StartUpActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/LauncherActivity.java @@ -12,7 +12,7 @@ import lu.circl.mispbump.auxiliary.PreferenceManager; /** * Starts either the login or home activity. */ -public class StartUpActivity extends AppCompatActivity { +public class LauncherActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/lu/circl/mispbump/activities/LoginActivity.java b/app/src/main/java/lu/circl/mispbump/activities/LoginActivity.java index 851e3c8..37cdf3e 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/LoginActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/LoginActivity.java @@ -131,9 +131,9 @@ public class LoginActivity extends AppCompatActivity { mispRestClient.getMyUser(new MispRestClient.UserCallback() { @Override public void success(final User user) { - preferenceManager.setUserInfo(user); + preferenceManager.setMyUser(user); for (Role role : roles) { - if (role.getId().equals(user.role_id)) { + if (role.getId().equals(user.getRoleId())) { if (!role.getPermAdmin()) { progressBar.setVisibility(View.GONE); Snackbar.make(constraintLayout, "No admin is associated with this authkey.", Snackbar.LENGTH_LONG).show(); @@ -142,10 +142,10 @@ public class LoginActivity extends AppCompatActivity { } } - mispRestClient.getOrganisation(user.org_id, new MispRestClient.OrganisationCallback() { + mispRestClient.getOrganisation(user.getRoleId(), new MispRestClient.OrganisationCallback() { @Override public void success(Organisation organisation) { - preferenceManager.setUserOrgInfo(organisation); + preferenceManager.setMyOrganisation(organisation); preferenceManager.setUserCredentials(url, authkey); progressBar.setVisibility(View.GONE); diff --git a/app/src/main/java/lu/circl/mispbump/activities/PreferenceActivity.java b/app/src/main/java/lu/circl/mispbump/activities/PreferenceActivity.java index 7af22e5..fedd24a 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/PreferenceActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/PreferenceActivity.java @@ -8,17 +8,24 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import lu.circl.mispbump.R; +import lu.circl.mispbump.auxiliary.PreferenceManager; public class PreferenceActivity extends AppCompatActivity { + private PreferenceManager preferenceManager; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_preference); + + preferenceManager = PreferenceManager.getInstance(PreferenceActivity.this); + initializeViews(); } @@ -32,15 +39,27 @@ public class PreferenceActivity extends AppCompatActivity { FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - fragmentTransaction.add(R.id.fragmentContainer, new PreferencesFragment(), PreferencesFragment.class.getSimpleName()); + PreferencesFragment preferencesFragment = new PreferencesFragment(); + preferencesFragment.onDeleteAllSyncsListener = preference -> { + preferenceManager.clearUploadInformation(); + return true; + }; + + fragmentTransaction.add(R.id.fragmentContainer, preferencesFragment, PreferencesFragment.class.getSimpleName()); fragmentTransaction.commit(); } public static class PreferencesFragment extends PreferenceFragmentCompat { + + private Preference.OnPreferenceClickListener onDeleteAllSyncsListener; + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setPreferencesFromResource(R.xml.preference_screen_main, rootKey); - findPreference("PREF_DELETE_ALL_SYNCS").setOnPreferenceClickListener(preference -> true); + + Preference deleteAllSyncInfo = findPreference("PREF_DELETE_ALL_SYNCS"); + assert deleteAllSyncInfo != null; + deleteAllSyncInfo.setOnPreferenceClickListener(onDeleteAllSyncsListener); } } } diff --git a/app/src/main/java/lu/circl/mispbump/activities/ProfileActivity.java b/app/src/main/java/lu/circl/mispbump/activities/ProfileActivity.java index 58177ec..d87366b 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/ProfileActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/ProfileActivity.java @@ -1,7 +1,6 @@ package lu.circl.mispbump.activities; -import android.content.DialogInterface; import android.content.Intent; import android.graphics.Shader; import android.graphics.drawable.AnimatedVectorDrawable; @@ -45,6 +44,12 @@ public class ProfileActivity extends AppCompatActivity { private FloatingActionButton fab; private AnimatedVectorDrawable fabLoadingDrawable; + private View.OnClickListener onFabClicked = view -> { + fab.setImageDrawable(fabLoadingDrawable); + fabLoadingDrawable.start(); + updateProfileInformation(); + }; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -54,55 +59,12 @@ public class ProfileActivity extends AppCompatActivity { Pair credentials = preferenceManager.getUserCredentials(); mispRestClient = MispRestClient.getInstance(credentials.first, credentials.second); - init(); + initToolbar(); + initViews(); + populateInformationViews(); } - private void init() { - rootLayout = findViewById(R.id.rootLayout); - - ImageView headerBg = findViewById(R.id.headerBg); - headerBg.setImageDrawable(new TileDrawable(getRandomHeader(), Shader.TileMode.REPEAT)); - - // populate Toolbar (Actionbar) - Toolbar myToolbar = findViewById(R.id.toolbar); - setSupportActionBar(myToolbar); - - ActionBar ab = getSupportActionBar(); - if (ab != null) { - ab.setDisplayHomeAsUpEnabled(true); - ab.setDisplayShowTitleEnabled(true); - } - - fab = findViewById(R.id.fab); - fab.setOnClickListener(onFabClicked()); - - fabLoadingDrawable = (AnimatedVectorDrawable) getDrawable(R.drawable.animated_sync); - } - - private void populateInformationViews() { - Organisation organisation = preferenceManager.getUserOrganisation(); - - TextView name = findViewById(R.id.orgName); - name.setText(organisation.getName()); - - final MaterialPreferenceText uuid = findViewById(R.id.uuid); - uuid.setSubtitle(organisation.getUuid()); - - MaterialPreferenceText nationality = findViewById(R.id.nationality); - nationality.setSubtitle(organisation.getNationality()); - - MaterialPreferenceText sector = findViewById(R.id.sector); - if (organisation.getSector() == null) { - sector.setVisibility(View.GONE); - } else { - sector.setSubtitle(organisation.getSector()); - } - - MaterialPreferenceText description = findViewById(R.id.description); - description.setSubtitle(organisation.getDescription()); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_profile, menu); @@ -119,23 +81,51 @@ public class ProfileActivity extends AppCompatActivity { return super.onOptionsItemSelected(item); } - private View.OnClickListener onFabClicked() { - return new View.OnClickListener() { - @Override - public void onClick(View v) { - fab.setImageDrawable(fabLoadingDrawable); - fabLoadingDrawable.start(); - updateProfile(); - } - }; + + private void initToolbar() { + Toolbar myToolbar = findViewById(R.id.toolbar); + setSupportActionBar(myToolbar); + + ActionBar ab = getSupportActionBar(); + if (ab != null) { + ab.setDisplayHomeAsUpEnabled(true); + ab.setDisplayShowTitleEnabled(true); + } } - private Drawable getRandomHeader() { - int[] ids = {R.drawable.ic_bank_note, R.drawable.ic_polka_dots, R.drawable.ic_wiggle, R.drawable.ic_circuit_board}; - return getDrawable(ids[new Random().nextInt(ids.length)]); + private void initViews() { + rootLayout = findViewById(R.id.rootLayout); + + ImageView headerBg = findViewById(R.id.headerBg); + headerBg.setImageDrawable(new TileDrawable(getRandomHeader(), Shader.TileMode.REPEAT)); + + fab = findViewById(R.id.fab); + fab.setOnClickListener(onFabClicked); + + fabLoadingDrawable = (AnimatedVectorDrawable) getDrawable(R.drawable.animated_sync); } - public void updateProfile() { + private void populateInformationViews() { + Organisation organisation = preferenceManager.getUserOrganisation(); + + TextView name = findViewById(R.id.orgName); + name.setText(organisation.getName()); + + final MaterialPreferenceText uuid = findViewById(R.id.uuid); + uuid.setSubtitle(organisation.getUuid()); + + MaterialPreferenceText nationality = findViewById(R.id.nationality); + nationality.setSubtitle(organisation.getNationality()); + + MaterialPreferenceText sector = findViewById(R.id.sector); + sector.setSubtitle(organisation.getSector()); + + MaterialPreferenceText description = findViewById(R.id.description); + description.setSubtitle(organisation.getDescription()); + } + + + public void updateProfileInformation() { mispRestClient.getRoles(new MispRestClient.AllRolesCallback() { @Override public void success(Role[] roles) { @@ -151,12 +141,12 @@ public class ProfileActivity extends AppCompatActivity { mispRestClient.getMyUser(new MispRestClient.UserCallback() { @Override public void success(final User user) { - preferenceManager.setUserInfo(user); - mispRestClient.getOrganisation(user.org_id, new MispRestClient.OrganisationCallback() { + preferenceManager.setMyUser(user); + mispRestClient.getOrganisation(user.getRoleId(), new MispRestClient.OrganisationCallback() { @Override public void success(Organisation organisation) { fabLoadingDrawable.stop(); - preferenceManager.setUserOrgInfo(organisation); + preferenceManager.setMyOrganisation(organisation); Snackbar.make(rootLayout, "Successfully update profile", Snackbar.LENGTH_SHORT).show(); } @@ -181,25 +171,23 @@ public class ProfileActivity extends AppCompatActivity { builder.setTitle("Clear all saved data and logout"); builder.setMessage("Do you really want to delete all data and logout?"); - builder.setNegativeButton("Discard", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.cancel(); - } - }); + builder.setNegativeButton("Discard", (dialog, which) -> dialog.cancel()); - builder.setPositiveButton("Delete & Logout", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - preferenceManager.clearAllData(); - KeyStoreWrapper.deleteAllStoredKeys(); + builder.setPositiveButton("Delete & Logout", (dialog, which) -> { + preferenceManager.clearAllData(); + KeyStoreWrapper.deleteAllStoredKeys(); - Intent login = new Intent(getApplicationContext(), LoginActivity.class); - startActivity(login); - finish(); - } + Intent login = new Intent(getApplicationContext(), LoginActivity.class); + startActivity(login); + finish(); }); builder.create().show(); } + + + private Drawable getRandomHeader() { + int[] ids = {R.drawable.ic_bank_note, R.drawable.ic_polka_dots, R.drawable.ic_wiggle, R.drawable.ic_circuit_board}; + return getDrawable(ids[new Random().nextInt(ids.length)]); + } } diff --git a/app/src/main/java/lu/circl/mispbump/activities/SyncInfoDetailActivity.java b/app/src/main/java/lu/circl/mispbump/activities/SyncInfoDetailActivity.java new file mode 100644 index 0000000..8722aaa --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/activities/SyncInfoDetailActivity.java @@ -0,0 +1,190 @@ +package lu.circl.mispbump.activities; + + +import android.animation.ValueAnimator; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroupOverlay; +import android.widget.CheckBox; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; + +import java.util.UUID; + +import lu.circl.mispbump.R; +import lu.circl.mispbump.auxiliary.PreferenceManager; +import lu.circl.mispbump.customViews.MaterialPasswordView; +import lu.circl.mispbump.customViews.MaterialPreferenceText; +import lu.circl.mispbump.models.SyncInformation; + + +public class SyncInfoDetailActivity extends AppCompatActivity { + + public static String EXTRA_SYNC_INFO_UUID = "EXTRA_SYNC_INFO_UUID"; + + private PreferenceManager preferenceManager; + private SyncInformation syncInformation; + + private boolean fabMenuExpanded; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sync_info_detail); + + preferenceManager = PreferenceManager.getInstance(SyncInfoDetailActivity.this); + syncInformation = preferenceManager.getSyncInformation(getExtraUuid()); + + if (syncInformation == null) { + throw new RuntimeException("Could not find UploadInformation with UUID {" + getExtraUuid().toString() + "}"); + } + + initToolbar(); + initFabMenu(); + populateContent(); + } + + @Override + protected void onPause() { + super.onPause(); + preferenceManager.addSyncInformation(syncInformation); + } + + + private UUID getExtraUuid() { + return (UUID) getIntent().getSerializableExtra(EXTRA_SYNC_INFO_UUID); + } + + private void initToolbar() { + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + ActionBar ab = getSupportActionBar(); + assert ab != null; + + ab.setDisplayHomeAsUpEnabled(true); + } + + private void initFabMenu() { + FloatingActionButton fab = findViewById(R.id.fab_main); + FloatingActionButton fabUpload = findViewById(R.id.fab_upload); + FloatingActionButton fabDownload = findViewById(R.id.fab_download); + + LinearLayout uploadLayout = findViewById(R.id.layout_upload); + LinearLayout downloadLayout = findViewById(R.id.layout_download); + + uploadLayout.setVisibility(View.GONE); + downloadLayout.setVisibility(View.GONE); + + fab.setOnClickListener(view -> { + if (fabMenuExpanded) { + uploadLayout.setVisibility(View.GONE); + downloadLayout.setVisibility(View.GONE); + + fabMenuExpanded = false; + } else { + uploadLayout.setVisibility(View.VISIBLE); + downloadLayout.setVisibility(View.VISIBLE); + + fabMenuExpanded = true; + } + }); + + fabUpload.setOnClickListener(view -> { + + preferenceManager.addSyncInformation(syncInformation); + + Intent upload = new Intent(SyncInfoDetailActivity.this, UploadActivity.class); + upload.putExtra(UploadActivity.EXTRA_SYNC_INFO_UUID, syncInformation.getUuid().toString()); + startActivity(upload); + }); + + fabDownload.setOnClickListener(view -> { + + }); + } + + private void populateContent() { + + // information + + MaterialPreferenceText name = findViewById(R.id.name); + name.setSubtitle(syncInformation.getRemote().getOrganisation().getName()); + + MaterialPreferenceText uuid = findViewById(R.id.uuid); + uuid.setSubtitle(syncInformation.getRemote().getOrganisation().getUuid()); + + MaterialPreferenceText sector = findViewById(R.id.sector); + sector.setSubtitle(syncInformation.getRemote().getOrganisation().getSector()); + + MaterialPreferenceText description = findViewById(R.id.description); + description.setSubtitle(syncInformation.getRemote().getOrganisation().getDescription()); + + // settings + + CheckBox allowSelfSigned = findViewById(R.id.checkbox_self_signed); + allowSelfSigned.setChecked(syncInformation.getRemote().getServer().getSelfSigned()); + allowSelfSigned.setOnCheckedChangeListener((compoundButton, b) -> { + syncInformation.getRemote().getServer().setSelfSigned(b); + + }); + + CheckBox push = findViewById(R.id.checkbox_push); + push.setChecked(syncInformation.getRemote().getServer().getPush()); + push.setOnCheckedChangeListener((compoundButton, b) -> syncInformation.getRemote().getServer().setPush(b)); + + CheckBox pull = findViewById(R.id.checkbox_pull); + pull.setChecked(syncInformation.getRemote().getServer().getPull()); + pull.setOnCheckedChangeListener((compundButton, b) -> syncInformation.getRemote().getServer().setPull(b)); + + CheckBox cache = findViewById(R.id.checkbox_cache); + cache.setChecked(syncInformation.getRemote().getServer().getCachingEnabled()); + cache.setOnCheckedChangeListener((compoundButton, b) -> syncInformation.getRemote().getServer().setCachingEnabled(b)); + + // credentials + + MaterialPreferenceText email = findViewById(R.id.email); + email.setSubtitle(syncInformation.getLocal().getSyncUser().getEmail()); + + MaterialPasswordView password = findViewById(R.id.password); + password.setPassword(syncInformation.getLocal().getSyncUser().getPassword()); + + MaterialPasswordView authkey = findViewById(R.id.authkey); + authkey.setPassword(syncInformation.getLocal().getSyncUser().getAuthkey()); + } + + + public static void applyDim(@NonNull ViewGroup parent, float dimAmount) { +// ViewGroup root = (ViewGroup) getWindow().getDecorView().getRootView(); + Drawable dim = new ColorDrawable(Color.BLACK); + dim.setBounds(0, 0, parent.getWidth(), parent.getHeight()); + + ValueAnimator valueAnimator = ValueAnimator.ofFloat(dimAmount); + + valueAnimator.addUpdateListener(valueAnim -> { + float value = (float) valueAnim.getAnimatedValue(); + dim.setAlpha((int) (255 * value)); + ViewGroupOverlay overlay = parent.getOverlay(); + overlay.add(dim); + }); + + valueAnimator.start(); + } + + public static void clearDim(@NonNull ViewGroup parent) { + ViewGroupOverlay overlay = parent.getOverlay(); + overlay.clear(); + } +} 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 ef34c31..4f1fe1c 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/UploadActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/UploadActivity.java @@ -4,37 +4,92 @@ package lu.circl.mispbump.activities; import android.content.Intent; import android.os.Bundle; import android.view.MenuItem; -import android.view.View; -import androidx.annotation.Nullable; +import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.util.Pair; -import com.google.android.material.floatingactionbutton.FloatingActionButton; - import java.util.UUID; import lu.circl.mispbump.R; import lu.circl.mispbump.auxiliary.MispRestClient; import lu.circl.mispbump.auxiliary.PreferenceManager; -import lu.circl.mispbump.customViews.UploadAction; -import lu.circl.mispbump.models.UploadInformation; +import lu.circl.mispbump.customViews.ProgressActionView; +import lu.circl.mispbump.models.SyncInformation; import lu.circl.mispbump.models.restModels.Organisation; +import lu.circl.mispbump.models.restModels.Role; import lu.circl.mispbump.models.restModels.Server; import lu.circl.mispbump.models.restModels.User; public class UploadActivity extends AppCompatActivity { - public static String EXTRA_UPLOAD_INFO = "uploadInformation"; + public static final String EXTRA_SYNC_INFO_UUID = "EXTRA_SYNC_INFO_UUID"; private PreferenceManager preferenceManager; - private UploadInformation uploadInformation; + private MispRestClient mispRest; + private SyncInformation syncInformation; + + private ProgressActionView availableAction, organisationAction, userAction, serverAction; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_upload); + + preferenceManager = PreferenceManager.getInstance(UploadActivity.this); + + Pair credentials = preferenceManager.getUserCredentials(); + mispRest = MispRestClient.getInstance(credentials.first, credentials.second); + + parseExtra(); + initToolbar(); + initProgressActionViews(); + startUpload(); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + + return false; + } + + + private void parseExtra() { + Intent i = getIntent(); + String syncInfoUuid = i.getStringExtra(EXTRA_SYNC_INFO_UUID); + syncInformation = preferenceManager.getSyncInformation(UUID.fromString(syncInfoUuid)); + } + + private void initToolbar() { + Toolbar myToolbar = findViewById(R.id.toolbar); + setSupportActionBar(myToolbar); + + ActionBar ab = getSupportActionBar(); + if (ab != null) { + ab.setDisplayHomeAsUpEnabled(true); + ab.setDisplayShowTitleEnabled(true); + } + } + + private void initProgressActionViews() { + availableAction = findViewById(R.id.availableProgressAction); + organisationAction = findViewById(R.id.organisationProgressAction); + userAction = findViewById(R.id.userProgressAction); + serverAction = findViewById(R.id.serverProgressAction); + + availableAction.pending(); + organisationAction.pending(); + userAction.pending(); + serverAction.pending(); + } - private MispRestClient restClient; - private UploadAction availableAction, orgAction, userAction, serverAction; private MispRestClient.AvailableCallback availableCallback = new MispRestClient.AvailableCallback() { @Override @@ -92,187 +147,70 @@ public class UploadActivity extends AppCompatActivity { } }; - private FloatingActionButton fab; - - private boolean errorWhileUpload; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_upload); - - preferenceManager = PreferenceManager.getInstance(UploadActivity.this); - Pair credentials = preferenceManager.getUserCredentials(); - restClient = MispRestClient.getInstance(credentials.first, credentials.second); - - parseExtra(); - 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() { - 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 initViews() { - getWindow().setStatusBarColor(getColor(R.color.colorPrimary)); - - fab = findViewById(R.id.fab); - fab.hide(); - - // toolbar - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - ActionBar ab = getSupportActionBar(); - assert ab != null; - - ab.setDisplayShowTitleEnabled(false); - ab.setDisplayHomeAsUpEnabled(true); - ab.setHomeAsUpIndicator(R.drawable.ic_close); - - availableAction = findViewById(R.id.availableAction); - orgAction = findViewById(R.id.orgAction); - userAction = findViewById(R.id.userAction); - serverAction = findViewById(R.id.serverAction); - } - - private void saveCurrentState() { - if (errorWhileUpload) { - uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE); - } - preferenceManager.addUploadInformation(uploadInformation); - } - - private void setUploadActionState(UploadAction uploadAction, UploadAction.UploadState state, @Nullable String error) { - uploadAction.setCurrentUploadState(state); - uploadAction.setError(error); - - switch (state) { - case PENDING: - if (fab.isShown()) { - fab.hide(); - } - break; - case LOADING: - errorWhileUpload = false; - if (fab.isShown()) { - fab.hide(); - } - break; - case DONE: - errorWhileUpload = false; - break; - case ERROR: - uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE); - - fab.setImageResource(R.drawable.ic_autorenew); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - setUploadActionState(availableAction, UploadAction.UploadState.LOADING, null); - startUpload(); - } - }); - if (!fab.isShown()) { - fab.show(); - } - errorWhileUpload = true; - break; - } - } - private User generateSyncUser(Organisation organisation) { - User syncUser = new User(); - syncUser.org_id = organisation.getId(); - syncUser.role_id = User.ROLE_SYNC_USER; - syncUser.email = uploadInformation.getRemote().syncUserEmail; - syncUser.password = uploadInformation.getRemote().syncUserPassword; - syncUser.authkey = uploadInformation.getRemote().syncUserAuthkey; - syncUser.termsaccepted = true; + User syncUser = syncInformation.getRemote().getSyncUser(); + + syncUser.setOrgId(organisation.getId()); + syncUser.setTermsAccepted(true); + + Role[] roles = preferenceManager.getRoles(); + for (Role role : roles) { + if (role.isSyncUserRole()) { + syncUser.setRoleId(role.getId()); + } + } return syncUser; } private Server generateSyncServer() { - Server server = new Server(); - server.name = uploadInformation.getRemote().organisation.getName() + "'s Sync Server"; - server.url = uploadInformation.getRemote().baseUrl; - server.remote_org_id = uploadInformation.getRemote().organisation.getId(); - server.authkey = uploadInformation.getLocal().syncUserAuthkey; - server.pull = uploadInformation.isPull(); - server.push = uploadInformation.isPush(); - server.caching_enabled = uploadInformation.isCached(); - server.self_signed = uploadInformation.isAllowSelfSigned(); + Server server = syncInformation.getRemote().getServer(); + server.setName(syncInformation.getRemote().getOrganisation().getName() + "'s Sync Server"); + server.setRemoteOrgId(syncInformation.getRemote().getOrganisation().getId()); + server.setAuthkey(syncInformation.getRemote().getSyncUser().getAuthkey()); + server.setPull(syncInformation.getRemote().getServer().getPull()); + server.setPush(syncInformation.getRemote().getServer().getPush()); + server.setCachingEnabled(syncInformation.getRemote().getServer().getCachingEnabled()); + server.setSelfSigned(syncInformation.getRemote().getServer().getCachingEnabled()); return server; } - /** - * Start upload to misp instance. - */ private void startUpload() { - availableAction.setCurrentUploadState(UploadAction.UploadState.LOADING); - restClient.isAvailable(availableCallback); + availableAction.start(); + mispRest.isAvailable(availableCallback); } private void mispAvailable(boolean available, String error) { if (available) { - setUploadActionState(availableAction, UploadAction.UploadState.DONE, null); - restClient.addOrganisation(uploadInformation.getRemote().organisation, organisationCallback); + availableAction.done(); + organisationAction.start(); + + mispRest.addOrganisation(syncInformation.getRemote().getOrganisation(), organisationCallback); } else { - setUploadActionState(availableAction, UploadAction.UploadState.ERROR, error); + availableAction.error(error); } } private void organisationAdded(Organisation organisation) { if (organisation != null) { - setUploadActionState(orgAction, UploadAction.UploadState.DONE, null); - uploadInformation.getRemote().organisation.setId(organisation.getId()); - restClient.addUser(generateSyncUser(organisation), userCallback); + organisationAction.done(); + userAction.start(); + + syncInformation.getRemote().getOrganisation().setId(organisation.getId()); + mispRest.addUser(generateSyncUser(organisation), userCallback); } else { - // search by UUID because the error does not give the actual ID - restClient.getOrganisation(uploadInformation.getRemote().organisation.getUuid(), new MispRestClient.OrganisationCallback() { + mispRest.getOrganisation(syncInformation.getRemote().getOrganisation().getUuid(), new MispRestClient.OrganisationCallback() { @Override public void success(Organisation organisation) { organisationAdded(organisation); + organisationAction.done("Organisation already on MISP instance"); } @Override public void failure(String error) { - setUploadActionState(orgAction, UploadAction.UploadState.ERROR, error); + organisationAction.error(error); } }); } @@ -280,18 +218,21 @@ public class UploadActivity extends AppCompatActivity { private void userAdded(User user) { if (user != null) { - setUploadActionState(userAction, UploadAction.UploadState.DONE, null); - restClient.getAllServers(allServersCallback); + userAction.done(); + serverAction.start(); + + mispRest.getAllServers(allServersCallback); } else { - restClient.getUser(uploadInformation.getRemote().syncUserEmail, new MispRestClient.UserCallback() { + mispRest.getUser(syncInformation.getLocal().getSyncUser().getEmail(), new MispRestClient.UserCallback() { @Override public void success(User user) { userAdded(user); + userAction.done("User already on MISP instance"); } @Override public void failure(String error) { - setUploadActionState(userAction, UploadAction.UploadState.ERROR, error); + userAction.error(error); } }); } @@ -302,35 +243,25 @@ public class UploadActivity extends AppCompatActivity { Server serverToUpload = generateSyncServer(); for (Server server : servers) { - if (server.remote_org_id.equals(serverToUpload.remote_org_id)) { + if (server.getRemoteOrgId().equals(serverToUpload.getRemoteOrgId())) { // server already exists: override id to update instead - serverToUpload.id = server.id; + serverToUpload.setId(server.getId()); break; } } - restClient.addServer(serverToUpload, serverCallback); + mispRest.addServer(serverToUpload, serverCallback); } else { - setUploadActionState(serverAction, UploadAction.UploadState.ERROR, "Could not retrieve server information"); + serverAction.error("Unknown error while creating the Sync Server"); } } private void serverAdded(Server server) { if (server != null) { - setUploadActionState(serverAction, UploadAction.UploadState.DONE, null); - uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.COMPLETE); - saveCurrentState(); - - fab.setImageResource(R.drawable.ic_check); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); - fab.show(); + serverAction.done(); + preferenceManager.addSyncInformation(syncInformation); } else { - setUploadActionState(serverAction, UploadAction.UploadState.ERROR, "Could not add server"); + serverAction.error("Could not create Sync Server"); } } } diff --git a/app/src/main/java/lu/circl/mispbump/activities/UploadInfoActivity.java b/app/src/main/java/lu/circl/mispbump/activities/UploadInfoActivity.java deleted file mode 100644 index 66067cb..0000000 --- a/app/src/main/java/lu/circl/mispbump/activities/UploadInfoActivity.java +++ /dev/null @@ -1,269 +0,0 @@ -package lu.circl.mispbump.activities; - - -import android.content.Intent; -import android.os.Bundle; -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.DialogManager; -import lu.circl.mispbump.auxiliary.PreferenceManager; -import lu.circl.mispbump.fragments.UploadCredentialsFragment; -import lu.circl.mispbump.fragments.UploadSettingsFragment; -import lu.circl.mispbump.models.UploadInformation; - - -public class UploadInfoActivity extends AppCompatActivity { - - public static String EXTRA_UPLOAD_INFO_UUID = "uploadInformationUuid"; - - private PreferenceManager preferenceManager; - private UploadInformation uploadInformation; - private ViewPagerAdapter viewPagerAdapter; - - private FloatingActionButton fab; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_upload_information); - - preferenceManager = PreferenceManager.getInstance(UploadInfoActivity.this); - - // tint statusBar - getWindow().setStatusBarColor(getColor(R.color.colorPrimary)); -// getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - - parseExtra(); - initToolbar(); - initViewPager(); - initViews(); - } - - @Override - protected void onResume() { - super.onResume(); - - // refresh current uploadInformation - if (uploadInformation != null) { - uploadInformation = preferenceManager.getUploadInformation(uploadInformation.getUuid()); - initContent(); - } - } - - @Override - public void onBackPressed() { - super.onBackPressed(); - saveCurrentSettings(); - } - - @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: - DialogManager.deleteSyncInformationDialog(UploadInfoActivity.this, new DialogManager.IDialogFeedback() { - @Override - public void positive() { - preferenceManager.removeUploadInformation(uploadInformation.getUuid()); - finish(); - } - - @Override - public void negative() { - } - }); - - return true; - - case android.R.id.home: - saveCurrentSettings(); - finish(); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } - - - 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 toolbarTitle = findViewById(R.id.toolbarTitle); - toolbarTitle.setText(uploadInformation.getRemote().organisation.getName()); - - ab.setHomeAsUpIndicator(R.drawable.ic_close); - - ab.setDisplayShowTitleEnabled(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); - - viewPager.addOnPageChangeListener(onPageChangeListener()); - - tabLayout.setupWithViewPager(viewPager); - } - - private void initViews() { - fab = findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - // for the UploadActivity to have the latest settings of this UploadInfoObject - saveCurrentSettings(); - - Intent i = new Intent(UploadInfoActivity.this, UploadActivity.class); - i.putExtra(UploadActivity.EXTRA_UPLOAD_INFO, uploadInformation.getUuid()); - startActivity(i); - } - }); - } - - private void initContent() { - switch (uploadInformation.getCurrentSyncStatus()) { - case COMPLETE: - - break; - - case FAILURE: - - break; - - case PENDING: - - break; - - default: - - break; - } - } - - 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); - } - - - private ViewPager.OnPageChangeListener onPageChangeListener() { - return new ViewPager.OnPageChangeListener() { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - if (position == 0) { - float scale = (1 - positionOffset); - fab.setScaleX(scale); - fab.setScaleY(scale); - } - } - - @Override - public void onPageSelected(int position) { - - } - - @Override - public void onPageScrollStateChanged(int state) { - - } - }; - } - - - class ViewPagerAdapter extends FragmentPagerAdapter { - - private UploadSettingsFragment uploadSettingsFragment; - private UploadCredentialsFragment uploadCredentialsFragment; - - ViewPagerAdapter(@NonNull FragmentManager fm, UploadInformation uploadInformation) { - super(fm, ViewPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); - uploadSettingsFragment = new UploadSettingsFragment(uploadInformation); - uploadCredentialsFragment = new UploadCredentialsFragment(uploadInformation); - } - - @NonNull - @Override - public Fragment getItem(int position) { - switch (position) { - case 0: - return uploadSettingsFragment; - - case 1: - return uploadCredentialsFragment; - - default: - uploadSettingsFragment = new UploadSettingsFragment(); - return uploadSettingsFragment; - } - } - - @Nullable - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return getString(R.string.settings); - - case 1: - return getString(R.string.credentials); - - default: - return "N/A"; - } - } - - @Override - public int getCount() { - return 2; - } - } -} diff --git a/app/src/main/java/lu/circl/mispbump/adapters/SyncInfoAdapter.java b/app/src/main/java/lu/circl/mispbump/adapters/SyncInfoAdapter.java new file mode 100644 index 0000000..4586d91 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/adapters/SyncInfoAdapter.java @@ -0,0 +1,103 @@ +package lu.circl.mispbump.adapters; + + +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.recyclerview.widget.RecyclerView; + +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +import lu.circl.mispbump.R; +import lu.circl.mispbump.interfaces.OnRecyclerItemClickListener; +import lu.circl.mispbump.models.SyncInformation; + + +public class SyncInfoAdapter extends RecyclerView.Adapter { + + private List items; + private OnRecyclerItemClickListener onRecyclerPositionClickListener; + + + @NonNull + @Override + public SyncInfoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { + View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_upload_information, viewGroup, false); + return new SyncInfoAdapter.ViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull final SyncInfoAdapter.ViewHolder holder, final int position) { + final SyncInformation item = items.get(position); + + SimpleDateFormat monthFormatter = new SimpleDateFormat("MMM", Locale.getDefault()); + SimpleDateFormat dayFormatter = new SimpleDateFormat("dd", Locale.getDefault()); + + holder.dateMonth.setText(monthFormatter.format(item.getSyncDate())); + holder.dateDay.setText(dayFormatter.format(item.getSyncDate())); + + holder.orgName.setText(item.getRemote().getOrganisation().getName()); + +// switch (item.getCurrentSyncStatus()) { +// case COMPLETE: +// ImageViewCompat.setImageTintList(holder.syncStatus, ColorStateList.valueOf(context.getColor(R.color.status_green))); +// holder.syncStatus.setImageResource(R.drawable.ic_check_outline); +// break; +// case FAILURE: +// ImageViewCompat.setImageTintList(holder.syncStatus, ColorStateList.valueOf(context.getColor(R.color.status_red))); +// holder.syncStatus.setImageResource(R.drawable.ic_error_outline); +// break; +// case PENDING: +// ImageViewCompat.setImageTintList(holder.syncStatus, ColorStateList.valueOf(context.getColor(R.color.status_amber))); +// holder.syncStatus.setImageResource(R.drawable.ic_pending); +// break; +// } + + holder.rootView.setOnClickListener(view -> onRecyclerPositionClickListener.onClick(view, position)); + } + + @Override + public int getItemCount() { + if (items == null) { + return 0; + } + + return items.size(); + } + + + public void setItems(List items) { + this.items = items; + notifyDataSetChanged(); + } + + public void setOnRecyclerPositionClickListener(OnRecyclerItemClickListener onRecyclerPositionClickListener) { + this.onRecyclerPositionClickListener = onRecyclerPositionClickListener; + } + + + static class ViewHolder extends RecyclerView.ViewHolder { + View rootView; + ImageView syncStatus; + TextView orgName, dateMonth, dateDay; + + ViewHolder(@NonNull View itemView) { + super(itemView); + + rootView = itemView; + + orgName = itemView.findViewById(R.id.orgName); + + dateMonth = itemView.findViewById(R.id.date_month); + dateDay = itemView.findViewById(R.id.date_day); + + syncStatus = itemView.findViewById(R.id.syncStatus); + } + } +} diff --git a/app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java b/app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java deleted file mode 100644 index 7ebcaf1..0000000 --- a/app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java +++ /dev/null @@ -1,119 +0,0 @@ -package lu.circl.mispbump.adapters; - - -import android.content.Context; -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; -import androidx.recyclerview.widget.RecyclerView; - -import java.util.List; - -import lu.circl.mispbump.R; -import lu.circl.mispbump.interfaces.OnRecyclerItemClickListener; -import lu.circl.mispbump.models.UploadInformation; - - -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; - } - - - @NonNull - @Override - 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 UploadInfoAdapter.ViewHolder(v); - } - - @Override - public void onBindViewHolder(@NonNull final UploadInfoAdapter.ViewHolder holder, final int position) { - - final UploadInformation item = items.get(position); - - holder.date.setText(item.getDateString()); - holder.orgName.setText(item.getRemote().organisation.getName()); - - switch (item.getCurrentSyncStatus()) { - case COMPLETE: - ImageViewCompat.setImageTintList(holder.syncStatus, ColorStateList.valueOf(context.getColor(R.color.status_green))); - holder.syncStatus.setImageResource(R.drawable.ic_check_outline); - break; - case FAILURE: - ImageViewCompat.setImageTintList(holder.syncStatus, ColorStateList.valueOf(context.getColor(R.color.status_red))); - holder.syncStatus.setImageResource(R.drawable.ic_error_outline); - break; - case PENDING: - ImageViewCompat.setImageTintList(holder.syncStatus, ColorStateList.valueOf(context.getColor(R.color.status_amber))); - holder.syncStatus.setImageResource(R.drawable.ic_pending); - break; - } - - holder.rootView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - onRecyclerItemClickListener.onClick(view, item); - } - }); - - holder.rootView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - onRecyclerPositionClickListener.onClick(view, position); - } - }); - } - - @Override - public int getItemCount() { - return items.size(); - } - - - public void setItems(List 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 9ded4b4..9a20a54 100644 --- a/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java +++ b/app/src/main/java/lu/circl/mispbump/auxiliary/DialogManager.java @@ -27,11 +27,11 @@ public class DialogManager { // this dialog needs definite user feedback adb.setCancelable(false); - if (oldSync.organisation.getName().equals(newSync.organisation.getName())) { - adb.setTitle("Already Synced with " + oldSync.organisation.getName()); - } else { - adb.setTitle("Already Synced with " + oldSync.organisation.getName() + "(Now:" + newSync.organisation.getName() + ")"); - } +// if (oldSync.organisation.getName().equals(newSync.organisation.getName())) { +// adb.setTitle("Already Synced with " + oldSync.organisation.getName()); +// } else { +// adb.setTitle("Already Synced with " + oldSync.organisation.getName() + "(Now:" + newSync.organisation.getName() + ")"); +// } adb.setMessage(""); @@ -143,33 +143,22 @@ public class DialogManager { final AlertDialog.Builder adb = new AlertDialog.Builder(context); adb.setTitle("Sync information received"); - adb.setMessage(syncInformation.organisation.getName()); - adb.setPositiveButton("Accept", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) { - callback.positive(); - } + adb.setMessage(syncInformation.getRemote().getOrganisation().getName()); + adb.setPositiveButton("Accept", (dialog, which) -> { + if (callback != null) { + callback.positive(); } }); - adb.setNegativeButton("Reject", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) { - callback.negative(); - } + adb.setNegativeButton("Reject", (dialog, which) -> { + if (callback != null) { + callback.negative(); } }); Activity act = (Activity) context; - act.runOnUiThread(new Runnable() { - @Override - public void run() { - adb.create().show(); - } - }); + act.runOnUiThread(() -> adb.create().show()); } /** @@ -183,31 +172,20 @@ public class DialogManager { adb.setTitle("Continue?"); adb.setMessage("Only continue if your partner already scanned this QR code"); - adb.setPositiveButton("Continue", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) { - callback.positive(); - } + adb.setPositiveButton("Continue", (dialog, which) -> { + if (callback != null) { + callback.positive(); } }); - adb.setNegativeButton("Show QR code again", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) { - callback.negative(); - } + adb.setNegativeButton("Show QR code again", (dialog, which) -> { + if (callback != null) { + callback.negative(); } }); Activity act = (Activity) context; - act.runOnUiThread(new Runnable() { - @Override - public void run() { - adb.create().show(); - } - }); + act.runOnUiThread(() -> adb.create().show()); } /** @@ -218,21 +196,11 @@ public class DialogManager { public static void loginHelpDialog(Context context) { final AlertDialog.Builder adb = new AlertDialog.Builder(context); adb.setMessage(R.string.login_help_text); - adb.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); + adb.setPositiveButton(R.string.ok, (dialog, which) -> dialog.dismiss()); Activity act = (Activity) context; - act.runOnUiThread(new Runnable() { - @Override - public void run() { - adb.create().show(); - } - }); + act.runOnUiThread(() -> adb.create().show()); } public static void instanceNotAvailableDialog(Context context, final IDialogFeedback callback) { @@ -240,31 +208,20 @@ public class DialogManager { adb.setTitle("MISP not available"); adb.setMessage("Your MISP instance is not available. Would you like to save?"); - adb.setPositiveButton("Retry now", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) { - callback.positive(); - } + adb.setPositiveButton("Retry now", (dialog, which) -> { + if (callback != null) { + callback.positive(); } }); - adb.setNegativeButton("Save & retry later", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) { - callback.negative(); - } + adb.setNegativeButton("Save & retry later", (dialog, which) -> { + if (callback != null) { + callback.negative(); } }); Activity act = (Activity) context; - act.runOnUiThread(new Runnable() { - @Override - public void run() { - adb.create().show(); - } - }); + act.runOnUiThread(() -> adb.create().show()); } public static void deleteSyncInformationDialog(Context context, final IDialogFeedback callback) { @@ -272,31 +229,20 @@ public class DialogManager { adb.setTitle("Delete Sync Information?"); adb.setMessage("This sync information will be deleted permanently"); - adb.setPositiveButton("Delete", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) { - callback.positive(); - } + adb.setPositiveButton("Delete", (dialog, which) -> { + if (callback != null) { + callback.positive(); } }); - adb.setNegativeButton("Discard", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) { - callback.negative(); - } + adb.setNegativeButton("Discard", (dialog, which) -> { + if (callback != null) { + callback.negative(); } }); Activity act = (Activity) context; - act.runOnUiThread(new Runnable() { - @Override - public void run() { - adb.create().show(); - } - }); + act.runOnUiThread(() -> adb.create().show()); } /** @@ -307,6 +253,4 @@ public class DialogManager { void negative(); } - - } diff --git a/app/src/main/java/lu/circl/mispbump/auxiliary/MispRestClient.java b/app/src/main/java/lu/circl/mispbump/auxiliary/MispRestClient.java index 75c5941..68350ed 100644 --- a/app/src/main/java/lu/circl/mispbump/auxiliary/MispRestClient.java +++ b/app/src/main/java/lu/circl/mispbump/auxiliary/MispRestClient.java @@ -5,20 +5,17 @@ import android.annotation.SuppressLint; import androidx.annotation.NonNull; -import java.io.IOException; import java.net.NoRouteToHostException; import java.security.cert.CertificateException; import java.util.List; -import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import lu.circl.mispbump.interfaces.MispRestInterface; +import lu.circl.mispbump.interfaces.MispService; import lu.circl.mispbump.models.restModels.MispOrganisation; import lu.circl.mispbump.models.restModels.MispRole; import lu.circl.mispbump.models.restModels.MispServer; @@ -28,7 +25,6 @@ import lu.circl.mispbump.models.restModels.Role; import lu.circl.mispbump.models.restModels.Server; import lu.circl.mispbump.models.restModels.User; import lu.circl.mispbump.models.restModels.Version; -import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.logging.HttpLoggingInterceptor; @@ -47,7 +43,7 @@ public class MispRestClient { private static MispRestClient instance; - private MispRestInterface mispRestInterface; + private MispService mispService; public static MispRestClient getInstance(String url, String authkey) { if (instance == null) { @@ -67,13 +63,18 @@ public class MispRestClient { .client(getCustomClient(true, true, authkey)) .build(); - mispRestInterface = retrofit.create(MispRestInterface.class); + mispService = retrofit.create(MispService.class); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } } + public MispService getService() { + return mispService; + } + + /** * @param unsafe whether to accept all certificates or only trusted ones * @param logging whether to log Retrofit calls (for debugging) @@ -90,12 +91,14 @@ public class MispRestClient { new X509TrustManager() { @SuppressLint("TrustAllX509TrustManager") @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) { + // nothing to do } @SuppressLint("TrustAllX509TrustManager") @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) { + // nothing to do } @Override @@ -113,12 +116,7 @@ public class MispRestClient { final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]); - builder.hostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }); + builder.hostnameVerifier((hostname, session) -> true); } if (logging) { @@ -127,16 +125,13 @@ public class MispRestClient { builder.addInterceptor(interceptor); } - // create authorization interceptor - builder.addInterceptor(new Interceptor() { - @Override - public okhttp3.Response intercept(Chain chain) throws IOException { - Request.Builder ongoing = chain.request().newBuilder(); - ongoing.addHeader("Accept", "application/json"); - ongoing.addHeader("Content-Type", "application/json"); - ongoing.addHeader("Authorization", authkey); - return chain.proceed(ongoing.build()); - } + // create interceptor + builder.addInterceptor(chain -> { + Request.Builder ongoing = chain.request().newBuilder(); + ongoing.addHeader("Accept", "application/json"); + ongoing.addHeader("Content-Type", "application/json"); + ongoing.addHeader("Authorization", authkey); + return chain.proceed(ongoing.build()); }); return builder.build(); @@ -153,7 +148,7 @@ public class MispRestClient { * @param callback {@link AvailableCallback} */ public void isAvailable(final AvailableCallback callback) { - Call call = mispRestInterface.pyMispVersion(); + Call call = mispService.pyMispVersion(); call.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { @@ -177,10 +172,10 @@ public class MispRestClient { } public void getRoles(final AllRolesCallback callback) { - Call> call = mispRestInterface.getRoles(); + Call> call = mispService.getRoles(); call.enqueue(new Callback>() { @Override - public void onResponse(Call> call, Response> response) { + public void onResponse(@NonNull Call> call, @NonNull Response> response) { if (!response.isSuccessful()) { callback.failure(extractError(response)); @@ -200,7 +195,7 @@ public class MispRestClient { } @Override - public void onFailure(Call> call, Throwable t) { + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { callback.failure(extractError(t)); } }); @@ -213,7 +208,7 @@ public class MispRestClient { * @param callback {@link UserCallback} wrapper to return user directly */ public void getMyUser(final UserCallback callback) { - Call call = mispRestInterface.getMyUserInformation(); + Call call = mispService.getMyUserInformation(); call.enqueue(new Callback() { @Override @@ -222,7 +217,7 @@ public class MispRestClient { callback.failure(extractError(response)); } else { if (response.body() != null) { - callback.success(response.body().user); + callback.success(response.body().getUser()); } else { callback.failure("response body was null"); } @@ -243,9 +238,8 @@ public class MispRestClient { * @param userId user identifier * @param callback {@link UserCallback} wrapper to return user directly */ - public void getUser(int userId, final UserCallback callback) { - Call call = mispRestInterface.getUser(userId); + Call call = mispService.getUser(userId); call.enqueue(new Callback() { @Override @@ -254,7 +248,7 @@ public class MispRestClient { callback.failure(extractError(response)); } else { if (response.body() != null) { - callback.success(response.body().user); + callback.success(response.body().getUser()); } else { callback.failure("response body was null"); } @@ -274,7 +268,7 @@ public class MispRestClient { @Override public void success(User[] users) { for (User user : users) { - if (user.email.equals(emailAddress)) { + if (user.getEmail().equals(emailAddress)) { callback.success(user); return; } @@ -290,8 +284,29 @@ public class MispRestClient { }); } + public void getAllUsers(final AllMispUsersCallback callback) { + Call> call = mispService.getAllUsers(); + + call.enqueue(new Callback>() { + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + if (!response.isSuccessful()) { + callback.failure("Failed onResponse"); + return; + } + + callback.success(response.body()); + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + callback.failure(extractError(t)); + } + }); + } + public void getAllUsers(final AllUsersCallback callback) { - Call> call = mispRestInterface.getAllUsers(); + Call> call = mispService.getAllUsers(); call.enqueue(new Callback>() { @Override @@ -307,7 +322,7 @@ public class MispRestClient { User[] users = new User[mispUsers.size()]; for (int i = 0; i < users.length; i++) { - users[i] = mispUsers.get(i).user; + users[i] = mispUsers.get(i).getUser(); } callback.success(users); @@ -327,7 +342,7 @@ public class MispRestClient { * @param callback {@link UserCallback} wrapper to return the created user directly */ public void addUser(User user, final UserCallback callback) { - Call call = mispRestInterface.addUser(user); + Call call = mispService.addUser(user); call.enqueue(new Callback() { @Override @@ -336,7 +351,7 @@ public class MispRestClient { callback.failure(extractError(response)); } else { assert response.body() != null; - callback.success(response.body().user); + callback.success(response.body().getUser()); } } @@ -357,7 +372,7 @@ public class MispRestClient { * @param callback {@link OrganisationCallback} wrapper to return a organisation directly */ public void getOrganisation(int orgId, final OrganisationCallback callback) { - Call call = mispRestInterface.getOrganisation(orgId); + Call call = mispService.getOrganisation(orgId); call.enqueue(new Callback() { @Override @@ -402,7 +417,7 @@ public class MispRestClient { } public void getAllOrganisations(final AllOrganisationsCallback callback) { - Call> call = mispRestInterface.getAllOrganisations(); + Call> call = mispService.getAllOrganisations(); call.enqueue(new Callback>() { @Override @@ -438,7 +453,7 @@ public class MispRestClient { * @param callback {@link OrganisationCallback} wrapper to return the created organisation directly */ public void addOrganisation(Organisation organisation, final OrganisationCallback callback) { - Call call = mispRestInterface.addOrganisation(organisation); + Call call = mispService.addOrganisation(organisation); call.enqueue(new Callback() { @Override @@ -466,7 +481,7 @@ public class MispRestClient { * @param callback {@link OrganisationCallback} wrapper to return a list of servers directly */ public void getAllServers(final AllServersCallback callback) { - Call> call = mispRestInterface.getAllServers(); + Call> call = mispService.getAllServers(); call.enqueue(new Callback>() { @Override @@ -474,16 +489,14 @@ public class MispRestClient { if (!response.isSuccessful()) { callback.failure(extractError(response)); } else { - List mispServers = response.body(); assert mispServers != null; Server[] servers = new Server[mispServers.size()]; for (int i = 0; i < servers.length; i++) { - servers[i] = mispServers.get(i).server; + servers[i] = mispServers.get(i).getServer(); } - callback.success(servers); } } @@ -495,6 +508,26 @@ public class MispRestClient { }); } + public void getAllServers(final AllRawServersCallback callback) { + Call> call = mispService.getAllServers(); + + call.enqueue(new Callback>() { + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + if (!response.isSuccessful()) { + callback.failure(extractError(response)); + } else { + callback.success(response.body()); + } + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + callback.failure(t.getMessage()); + } + }); + } + /** * Add a server to the MISP instance * @@ -502,7 +535,7 @@ public class MispRestClient { * @param callback {@link ServerCallback} wrapper to return the created server directly */ public void addServer(Server server, final ServerCallback callback) { - Call call = mispRestInterface.addServer(server); + Call call = mispService.addServer(server); call.enqueue(new Callback() { @Override @@ -588,7 +621,6 @@ public class MispRestClient { } // interfaces - public interface AvailableCallback { void available(); @@ -607,6 +639,12 @@ public class MispRestClient { void failure(String error); } + public interface AllMispUsersCallback { + void success(List users); + + void failure(String error); + } + public interface OrganisationCallback { void success(Organisation organisation); @@ -631,6 +669,12 @@ public class MispRestClient { void failure(String error); } + public interface AllRawServersCallback { + void success(List mispServers); + + void failure(String error); + } + public interface AllRolesCallback { void success(Role[] roles); 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 6bde257..e4622a2 100644 --- a/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java +++ b/app/src/main/java/lu/circl/mispbump/auxiliary/PreferenceManager.java @@ -21,7 +21,7 @@ import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; -import lu.circl.mispbump.models.UploadInformation; +import lu.circl.mispbump.models.SyncInformation; import lu.circl.mispbump.models.restModels.Organisation; import lu.circl.mispbump.models.restModels.Role; import lu.circl.mispbump.models.restModels.User; @@ -36,7 +36,7 @@ public class PreferenceManager { private static final String USER_INFOS = "user_infos"; private static final String USER_ORG_INFOS = "user_org_infos"; - private static final String UPLOAD_INFO = "upload_info"; + private static final String SYNC_INFO = "sync_info"; private static final String MISP_ROLES = "misp_roles"; @@ -83,10 +83,10 @@ public class PreferenceManager { public Role[] getRoles() { Type type = new TypeToken() { }.getType(); - String rolesString = preferences.getString(MISP_ROLES, ""); - assert rolesString != null; - if (rolesString.isEmpty()) { + String rolesString = preferences.getString(MISP_ROLES, null); + + if (rolesString == null) { return null; } else { return new Gson().fromJson(rolesString, type); @@ -99,23 +99,13 @@ public class PreferenceManager { * * @param user {@link User} */ - public void setUserInfo(User user) { + public void setMyUser(User user) { try { - String userStr = new Gson().toJson(user); - KeyStoreWrapper keyStoreWrapper = new KeyStoreWrapper(KeyStoreWrapper.USER_INFO_ALIAS); - String encryptedUserInfo = keyStoreWrapper.encrypt(userStr); SharedPreferences.Editor editor = preferences.edit(); - editor.putString(USER_INFOS, encryptedUserInfo); + KeyStoreWrapper keyStoreWrapper = new KeyStoreWrapper(KeyStoreWrapper.USER_INFO_ALIAS); + editor.putString(USER_INFOS, keyStoreWrapper.encrypt(new Gson().toJson(user))); 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) { + } catch (BadPaddingException | InvalidKeyException | NoSuchAlgorithmException | IllegalBlockSizeException | NoSuchPaddingException e) { e.printStackTrace(); } } @@ -135,17 +125,7 @@ public class PreferenceManager { KeyStoreWrapper keyStoreWrapper = new KeyStoreWrapper(KeyStoreWrapper.USER_INFO_ALIAS); String decrypted = keyStoreWrapper.decrypt(preferences.getString(USER_INFOS, "")); return new Gson().fromJson(decrypted, User.class); - } 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) { + } catch (BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchAlgorithmException | IllegalBlockSizeException | NoSuchPaddingException e) { e.printStackTrace(); } @@ -158,7 +138,7 @@ public class PreferenceManager { * * @param organisation Object representation of json organisation information */ - public void setUserOrgInfo(Organisation organisation) { + public void setMyOrganisation(Organisation organisation) { try { String orgStr = new Gson().toJson(organisation); KeyStoreWrapper keyStoreWrapper = new KeyStoreWrapper(KeyStoreWrapper.USER_ORGANISATION_INFO_ALIAS); @@ -243,58 +223,57 @@ public class PreferenceManager { } - private List cachedUploadInformationList; + private List cachedSyncInformationList; - private void loadUploadInformationList() { - KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS); - String storedUploadInfoString = preferences.getString(UPLOAD_INFO, null); + private void loadSyncInformationList() { + KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.SYNC_INFORMATION_ALIAS); + String storedSyncInfoString = preferences.getString(SYNC_INFO, null); - Type type = new TypeToken>() { - }.getType(); + Type type = new TypeToken>() {}.getType(); - if (storedUploadInfoString == null || storedUploadInfoString.isEmpty()) { - cachedUploadInformationList = new ArrayList<>(); + if (storedSyncInfoString == null || storedSyncInfoString.isEmpty()) { + cachedSyncInformationList = new ArrayList<>(); } else { try { - storedUploadInfoString = ksw.decrypt(storedUploadInfoString); - cachedUploadInformationList = new Gson().fromJson(storedUploadInfoString, type); + storedSyncInfoString = ksw.decrypt(storedSyncInfoString); + cachedSyncInformationList = new Gson().fromJson(storedSyncInfoString, type); } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException e) { e.printStackTrace(); } } } - private void saveUploadInformationList() { + private void saveSyncInformationList() { try { - KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS); - String cipherText = ksw.encrypt(new Gson().toJson(cachedUploadInformationList)); + KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.SYNC_INFORMATION_ALIAS); + String cipherText = ksw.encrypt(new Gson().toJson(cachedSyncInformationList)); SharedPreferences.Editor editor = preferences.edit(); - editor.putString(UPLOAD_INFO, cipherText); + editor.putString(SYNC_INFO, cipherText); editor.apply(); } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { e.printStackTrace(); } } - public List getUploadInformationList() { - if (cachedUploadInformationList == null) { - loadUploadInformationList(); + public List getSyncInformationList() { + if (cachedSyncInformationList == null) { + loadSyncInformationList(); } - return cachedUploadInformationList; + return cachedSyncInformationList; } - public void setUploadInformationList(List uploadInformationList) { - cachedUploadInformationList = uploadInformationList; - saveUploadInformationList(); + public void setSyncInformationList(List uploadInformationList) { + cachedSyncInformationList = uploadInformationList; + saveSyncInformationList(); } - public UploadInformation getUploadInformation(UUID uuid) { - if (cachedUploadInformationList == null) { - loadUploadInformationList(); + public SyncInformation getSyncInformation(UUID uuid) { + if (cachedSyncInformationList == null) { + loadSyncInformationList(); } - for (UploadInformation ui : cachedUploadInformationList) { + for (SyncInformation ui : cachedSyncInformationList) { if (ui.getUuid().compareTo(uuid) == 0) { return ui; } @@ -303,51 +282,50 @@ public class PreferenceManager { return null; } - public void addUploadInformation(UploadInformation uploadInformation) { - if (cachedUploadInformationList == null) { - loadUploadInformationList(); + public void addSyncInformation(SyncInformation syncInformation) { + if (cachedSyncInformationList == null) { + loadSyncInformationList(); } // 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(); + for (int i = 0; i < cachedSyncInformationList.size(); i++) { + if (cachedSyncInformationList.get(i).getUuid().compareTo(syncInformation.getUuid()) == 0) { + cachedSyncInformationList.set(i, syncInformation); + saveSyncInformationList(); return; } } - // else: add - cachedUploadInformationList.add(uploadInformation); - saveUploadInformationList(); + cachedSyncInformationList.add(syncInformation); + saveSyncInformationList(); } public void removeUploadInformation(UUID uuid) { - if (cachedUploadInformationList == null) { - loadUploadInformationList(); + if (cachedSyncInformationList == null) { + loadSyncInformationList(); } - for (UploadInformation ui : cachedUploadInformationList) { + for (SyncInformation ui : cachedSyncInformationList) { if (ui.getUuid().compareTo(uuid) == 0) { // if is last element, then clear everything including IV and key in KeyStore - if (cachedUploadInformationList.size() == 1) { + if (cachedSyncInformationList.size() == 1) { clearUploadInformation(); } else { - cachedUploadInformationList.remove(ui); - saveUploadInformationList(); + cachedSyncInformationList.remove(ui); + saveSyncInformationList(); } } } } public void clearUploadInformation() { - cachedUploadInformationList.clear(); + cachedSyncInformationList.clear(); - KeyStoreWrapper keyStoreWrapper = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS); + KeyStoreWrapper keyStoreWrapper = new KeyStoreWrapper(KeyStoreWrapper.SYNC_INFORMATION_ALIAS); keyStoreWrapper.deleteStoredKey(); SharedPreferences.Editor editor = preferences.edit(); - editor.remove(UPLOAD_INFO); + editor.remove(SYNC_INFO); editor.apply(); } diff --git a/app/src/main/java/lu/circl/mispbump/customViews/MaterialPasswordView.java b/app/src/main/java/lu/circl/mispbump/customViews/MaterialPasswordView.java index ae6c3d7..9e90a9b 100644 --- a/app/src/main/java/lu/circl/mispbump/customViews/MaterialPasswordView.java +++ b/app/src/main/java/lu/circl/mispbump/customViews/MaterialPasswordView.java @@ -18,7 +18,6 @@ import lu.circl.mispbump.R; public class MaterialPasswordView extends ConstraintLayout { private TextView titleView, passwordView; - private OnCopyClickListener onCopyClickListener; public MaterialPasswordView(Context context, AttributeSet attrs) { @@ -31,14 +30,6 @@ public class MaterialPasswordView extends ConstraintLayout { final String password = a.getString(R.styleable.MaterialPasswordView_password); a.recycle(); - ImageButton copyButton = view.findViewById(R.id.copy); - copyButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - onCopyClickListener.onClick(title, getPassword()); - } - }); - titleView = view.findViewById(R.id.material_password_title); titleView.setText(title); @@ -47,14 +38,11 @@ public class MaterialPasswordView extends ConstraintLayout { passwordView.setText(password); ImageButton visibleToggle = findViewById(R.id.visibleToggle); - visibleToggle.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (passwordView.getTransformationMethod() == null) { - passwordView.setTransformationMethod(new PasswordTransformationMethod()); - } else { - passwordView.setTransformationMethod(null); - } + visibleToggle.setOnClickListener(v -> { + if (passwordView.getTransformationMethod() == null) { + passwordView.setTransformationMethod(new PasswordTransformationMethod()); + } else { + passwordView.setTransformationMethod(null); } }); } @@ -84,13 +72,4 @@ public class MaterialPasswordView extends ConstraintLayout { } } - - public void addOnCopyClickedListener(OnCopyClickListener listener) { - onCopyClickListener = listener; - } - - - public interface OnCopyClickListener { - void onClick(String title, String password); - } } diff --git a/app/src/main/java/lu/circl/mispbump/customViews/ProgressActionView.java b/app/src/main/java/lu/circl/mispbump/customViews/ProgressActionView.java new file mode 100644 index 0000000..daa9171 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/customViews/ProgressActionView.java @@ -0,0 +1,146 @@ +package lu.circl.mispbump.customViews; + + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import lu.circl.mispbump.R; + + +public class ProgressActionView extends LinearLayout { + + private Context context; + + private ImageView icon; + private ProgressBar progressBar; + private TextView title, feedback; + + private Drawable pendingIcon, doneIcon, errorIcon; + + public ProgressActionView(Context context) { + this(context, null); + } + public ProgressActionView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + public ProgressActionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + public ProgressActionView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + this.context = context; + initViews(attrs); + } + + + private void initViews(AttributeSet attrs) { + View v = LayoutInflater.from(context).inflate(R.layout.view_upload_action, this, true); + + icon = v.findViewById(R.id.progressIcon); + progressBar = v.findViewById(R.id.progressBar); + title = v.findViewById(R.id.title); + feedback = v.findViewById(R.id.error); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProgressActionView); + title.setText(a.getString(R.styleable.ProgressActionView_action_title)); + pendingIcon = context.getDrawable(a.getResourceId(R.styleable.ProgressActionView_action_pending_icon, 0)); + doneIcon = context.getDrawable(a.getResourceId(R.styleable.ProgressActionView_action_done_icon, 0)); + errorIcon = context.getDrawable(a.getResourceId(R.styleable.ProgressActionView_action_error_icon, 0)); + a.recycle(); + + pendingIcon.setTint(context.getColor(R.color.status_amber)); + doneIcon.setTint(context.getColor(R.color.status_green)); + errorIcon.setTint(context.getColor(R.color.status_red)); + + pending(); + icon.setImageTintList(ColorStateList.valueOf(context.getColor(R.color.status_amber))); + } + + + public void pending() { + progressBar.setVisibility(GONE); + switchIcon(pendingIcon, R.color.status_amber); + icon.setVisibility(VISIBLE); + } + + public void start() { + progressBar.setVisibility(VISIBLE); + + icon.setVisibility(GONE); + feedback.setVisibility(GONE); + } + + public void done() { + done(""); + } + + public void done(String message) { + progressBar.setVisibility(GONE); + + switchIcon(doneIcon, R.color.status_green); + icon.setVisibility(VISIBLE); + + if (message.isEmpty()) { + feedback.setVisibility(GONE); + } else { + feedback.setTextColor(context.getColor(R.color.status_amber)); + feedback.setText(message); + feedback.setVisibility(VISIBLE); + } + } + + public void error(String error) { + progressBar.setVisibility(GONE); + + switchIcon(errorIcon, R.color.status_red); + icon.setVisibility(VISIBLE); + + feedback.setTextColor(context.getColor(R.color.status_red)); + feedback.setText(error); + feedback.setVisibility(VISIBLE); + } + + public void info(String info) { + progressBar.setVisibility(GONE); + + switchIcon(errorIcon, R.color.status_amber); + icon.setVisibility(VISIBLE); + + this.feedback.setTextColor(context.getColor(R.color.status_amber)); + this.feedback.setText(info); + this.feedback.setVisibility(VISIBLE); + } + + + private void switchIcon(Drawable d, int color) { + icon.setImageDrawable(d); + icon.setImageTintList(ColorStateList.valueOf(context.getColor(color))); + } + + public void setTitle(String title) { + this.title.setText(title); + } + + public void setErrorIconDrawable(Drawable d) { + errorIcon = d; + } + + public void setPendingIconDrawable(Drawable d) { + pendingIcon = d; + } + + public void setDoneIconDrawable(Drawable d) { + doneIcon = d; + } +} 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 4b53dba..a821ca7 100644 --- a/app/src/main/java/lu/circl/mispbump/customViews/UploadAction.java +++ b/app/src/main/java/lu/circl/mispbump/customViews/UploadAction.java @@ -3,7 +3,6 @@ package lu.circl.mispbump.customViews; import android.content.Context; import android.content.res.ColorStateList; -import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -45,15 +44,15 @@ public class UploadAction extends ConstraintLayout { View baseView = LayoutInflater.from(context).inflate(R.layout.view_upload_action, this); - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UploadAction); +// TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UploadAction); +// +// titleView = baseView.findViewById(R.id.title); +// titleView.setText(a.getString(R.styleable.UploadAction_description)); - titleView = baseView.findViewById(R.id.title); - titleView.setText(a.getString(R.styleable.UploadAction_description)); - - a.recycle(); +// a.recycle(); errorView = baseView.findViewById(R.id.error); - stateView = baseView.findViewById(R.id.stateView); + stateView = baseView.findViewById(R.id.progressIcon); progressBar = baseView.findViewById(R.id.progressBar); setCurrentUploadState(UploadState.PENDING); diff --git a/app/src/main/java/lu/circl/mispbump/fragments/UploadCredentialsFragment.java b/app/src/main/java/lu/circl/mispbump/fragments/UploadCredentialsFragment.java deleted file mode 100644 index d19029c..0000000 --- a/app/src/main/java/lu/circl/mispbump/fragments/UploadCredentialsFragment.java +++ /dev/null @@ -1,68 +0,0 @@ -package lu.circl.mispbump.fragments; - - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.fragment.app.Fragment; - -import com.google.android.material.snackbar.Snackbar; - -import lu.circl.mispbump.R; -import lu.circl.mispbump.customViews.MaterialPasswordView; -import lu.circl.mispbump.customViews.MaterialPreferenceText; -import lu.circl.mispbump.models.UploadInformation; - - -public class UploadCredentialsFragment extends Fragment { - - private View rootLayout; - private UploadInformation uploadInformation; - - public UploadCredentialsFragment() { - } - - public UploadCredentialsFragment(UploadInformation uploadInformation) { - this.uploadInformation = uploadInformation; - } - - private MaterialPasswordView.OnCopyClickListener onCopyClickListener = new MaterialPasswordView.OnCopyClickListener() { - @Override - public void onClick(String title, String password) { - ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(title, password); - clipboard.setPrimaryClip(clip); - Snackbar.make(rootLayout, "Copied to clipboard", Snackbar.LENGTH_LONG).show(); - } - }; - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_upload_credentials, container, false); - - rootLayout = v.findViewById(R.id.rootLayout); - - MaterialPreferenceText baseUrl = v.findViewById(R.id.baseUrl); - baseUrl.setSubtitle(uploadInformation.getRemote().baseUrl); - - MaterialPreferenceText email = v.findViewById(R.id.email); - email.setSubtitle(uploadInformation.getLocal().syncUserEmail); - - MaterialPasswordView authkey = v.findViewById(R.id.authkey); - authkey.setPassword(uploadInformation.getRemote().syncUserAuthkey); - authkey.addOnCopyClickedListener(onCopyClickListener); - - MaterialPasswordView password = v.findViewById(R.id.password); - password.setPassword(uploadInformation.getRemote().syncUserPassword); - password.addOnCopyClickedListener(onCopyClickListener); - - return v; - } - -} diff --git a/app/src/main/java/lu/circl/mispbump/fragments/UploadSettingsFragment.java b/app/src/main/java/lu/circl/mispbump/fragments/UploadSettingsFragment.java deleted file mode 100644 index 021c44a..0000000 --- a/app/src/main/java/lu/circl/mispbump/fragments/UploadSettingsFragment.java +++ /dev/null @@ -1,89 +0,0 @@ -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; -import lu.circl.mispbump.customViews.MaterialPreferenceSwitch; -import lu.circl.mispbump.models.UploadInformation; - - -public class UploadSettingsFragment extends Fragment { - - 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_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); - } - - public boolean getPush() { - return push.isChecked(); - } - - public void setPush(boolean push) { - this.push.setChecked(push); - } - - public boolean getPull() { - return pull.isChecked(); - } - - public void setPull(boolean pull) { - this.pull.setChecked(pull); - } - - 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/interfaces/MispRestInterface.java b/app/src/main/java/lu/circl/mispbump/interfaces/MispService.java similarity index 97% rename from app/src/main/java/lu/circl/mispbump/interfaces/MispRestInterface.java rename to app/src/main/java/lu/circl/mispbump/interfaces/MispService.java index 68ce5e6..cc8852a 100644 --- a/app/src/main/java/lu/circl/mispbump/interfaces/MispRestInterface.java +++ b/app/src/main/java/lu/circl/mispbump/interfaces/MispService.java @@ -21,7 +21,7 @@ import retrofit2.http.Path; /** * RetroFit2 interface for communication with misp instances */ -public interface MispRestInterface { +public interface MispService { // settings routes diff --git a/app/src/main/java/lu/circl/mispbump/models/ExchangeInformation.java b/app/src/main/java/lu/circl/mispbump/models/ExchangeInformation.java new file mode 100644 index 0000000..0a31f2e --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/models/ExchangeInformation.java @@ -0,0 +1,44 @@ +package lu.circl.mispbump.models; + + +import androidx.annotation.NonNull; + +import lu.circl.mispbump.models.restModels.Organisation; +import lu.circl.mispbump.models.restModels.Server; +import lu.circl.mispbump.models.restModels.User; + + +public class ExchangeInformation { + + private Organisation organisation; + private User syncUser; + private Server server; + + public Organisation getOrganisation() { + return organisation; + } + public void setOrganisation(Organisation organisation) { + this.organisation = organisation; + } + + public User getSyncUser() { + return syncUser; + } + public void setSyncUser(User syncUser) { + this.syncUser = syncUser; + } + + public Server getServer() { + return server; + } + public void setServer(Server server) { + this.server = server; + } + + + @NonNull + @Override + public String toString() { + return "Exchange Information: \n" + organisation.toString() + "\n" + syncUser.toString() + "\n" + server.toString(); + } +} diff --git a/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java b/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java index 607f58b..93ffabd 100644 --- a/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java +++ b/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java @@ -1,32 +1,88 @@ package lu.circl.mispbump.models; -import lu.circl.mispbump.models.restModels.Organisation; +import androidx.annotation.NonNull; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.UUID; /** - * A Class that holds the information needed synchronize two misp instances. - * This class can be serialized and passed via QR code. + * Class that holds the information needed synchronize two misp instances. */ public class SyncInformation { - public Organisation organisation; - public String syncUserEmail; - public String syncUserPassword; - public String syncUserAuthkey; - public String baseUrl; + private UUID uuid; + private Date date, lastModified; + + private ExchangeInformation remote; + private ExchangeInformation local; + public SyncInformation() { + uuid = UUID.randomUUID(); + setSyncDate(); } + + public UUID getUuid() { + return uuid; + } + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + public Date getLastModified() { + return lastModified; + } + public void setLastModified() { + setLastModified(Calendar.getInstance().getTime()); + } + public void setLastModified(Date date) { + lastModified = date; + } + + public void setSyncDate() { + setSyncDate(Calendar.getInstance().getTime()); + } + public void setSyncDate(Date date) { + this.date = date; + this.lastModified = date; + } + public Date getSyncDate() { + return date; + } + private String getSyncDateString() { + SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()); + return df.format(date); + } + + + public ExchangeInformation getRemote() { + return remote; + } + public void setRemote(ExchangeInformation remote) { + this.remote = remote; + } + + public ExchangeInformation getLocal() { + return local; + } + public void setLocal(ExchangeInformation local) { + this.local = local; + } + + + @NonNull @Override public String toString() { - return "SyncInformation{" + - "organisation=" + organisation + - ", syncUserEmail='" + syncUserEmail + '\'' + - ", syncUserPassword='" + syncUserPassword + '\'' + - ", syncUserAuthkey='" + syncUserAuthkey + '\'' + - ", baseUrl='" + baseUrl + '\'' + - '}'; + return "Sync Information: \n" + + "UUID = " + uuid + "\n" + + "Sync Date = " + getSyncDateString() + "\n" + + remote.toString() + + local.toString(); } } diff --git a/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java b/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java deleted file mode 100644 index 3c73bdb..0000000 --- a/app/src/main/java/lu/circl/mispbump/models/UploadInformation.java +++ /dev/null @@ -1,130 +0,0 @@ -package lu.circl.mispbump.models; - - -import androidx.annotation.NonNull; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.UUID; - - -public class UploadInformation { - - public enum SyncStatus { - COMPLETE, - FAILURE, - PENDING - } - - private UUID uuid; - - private SyncStatus currentSyncStatus = SyncStatus.PENDING; - - private boolean allowSelfSigned, pull, push, cached; - - private SyncInformation local; - private SyncInformation remote; - - private Date date; - - public UploadInformation() { - uuid = UUID.randomUUID(); - date = Calendar.getInstance().getTime(); - } - - // getter and setter - - public void setCurrentSyncStatus(SyncStatus status) { - currentSyncStatus = status; - } - - public SyncStatus getCurrentSyncStatus() { - return currentSyncStatus; - } - - public void setLocal(SyncInformation local) { - this.local = local; - } - - public SyncInformation getLocal() { - return local; - } - - public void setRemote(SyncInformation remote) { - this.remote = remote; - } - - public SyncInformation getRemote() { - return remote; - } - - public UUID getUuid() { - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public void setDate() { - setDate(Calendar.getInstance().getTime()); - } - - public void setDate(Date date) { - this.date = date; - } - - public Date getDate() { - return date; - } - - public String getDateString() { - SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()); - return df.format(date); - } - - public boolean isAllowSelfSigned() { - return allowSelfSigned; - } - - public void setAllowSelfSigned(boolean allowSelfSigned) { - this.allowSelfSigned = allowSelfSigned; - } - - public boolean isPull() { - return pull; - } - - public void setPull(boolean pull) { - this.pull = pull; - } - - public boolean isPush() { - return push; - } - - public void setPush(boolean push) { - this.push = push; - } - - public boolean isCached() { - return cached; - } - - public void setCached(boolean cached) { - this.cached = cached; - } - - @NonNull - @Override - public String toString() { - return "UploadInformation{" + - "currentSyncStatus=" + currentSyncStatus + - ", local=" + local.toString() + - ", remote=" + remote.toString() + - ", date=" + date + - '}'; - } -} diff --git a/app/src/main/java/lu/circl/mispbump/models/restModels/MispServer.java b/app/src/main/java/lu/circl/mispbump/models/restModels/MispServer.java index efc2fcd..9ca3941 100644 --- a/app/src/main/java/lu/circl/mispbump/models/restModels/MispServer.java +++ b/app/src/main/java/lu/circl/mispbump/models/restModels/MispServer.java @@ -1,7 +1,8 @@ package lu.circl.mispbump.models.restModels; -import com.google.gson.annotations.Expose; +import androidx.annotation.NonNull; + import com.google.gson.annotations.SerializedName; import java.util.List; @@ -9,26 +10,50 @@ import java.util.List; public class MispServer { - public MispServer() { - } - - public MispServer(Server server, Organisation organisation, Organisation remoteOrganisation) { - this.server = server; - this.organisation = organisation; - this.remoteOrg = remoteOrganisation; - } - @SerializedName("Server") - @Expose - public Server server; - @SerializedName("Organisation") - @Expose - public Organisation organisation; - @SerializedName("RemoteOrg") - @Expose - public Organisation remoteOrg; - @SerializedName("User") - @Expose - public List user; + private Server server; + @SerializedName("Organisation") + private Organisation organisation; + + @SerializedName("RemoteOrg") + private Organisation remoteOrganisation; + + @SerializedName("User") + private List user; + + + public Server getServer() { + return server; + } + public void setServer(Server server) { + this.server = server; + } + + public Organisation getOrganisation() { + return organisation; + } + public void setOrganisation(Organisation organisation) { + this.organisation = organisation; + } + + public Organisation getRemoteOrganisation() { + return remoteOrganisation; + } + public void setRemoteOrganisation(Organisation remoteOrganisation) { + this.remoteOrganisation = remoteOrganisation; + } + + public List getUser() { + return user; + } + public void setUser(List user) { + this.user = user; + } + + @NonNull + @Override + public String toString() { + return server.toString() + "\n" + organisation.toString() + "\n" + remoteOrganisation.toString(); + } } diff --git a/app/src/main/java/lu/circl/mispbump/models/restModels/MispUser.java b/app/src/main/java/lu/circl/mispbump/models/restModels/MispUser.java index 5cd6855..426663f 100644 --- a/app/src/main/java/lu/circl/mispbump/models/restModels/MispUser.java +++ b/app/src/main/java/lu/circl/mispbump/models/restModels/MispUser.java @@ -1,17 +1,40 @@ package lu.circl.mispbump.models.restModels; -import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; public class MispUser { @SerializedName("User") - @Expose - public User user; + private User user; - public MispUser(User user) { + @SerializedName("Role") + private Role role; + + @SerializedName("Organisation") + private Organisation organisation; + + + public User getUser() { + return user; + } + public void setUser(User user) { this.user = user; } + + public Role getRole() { + return role; + } + public void setRole(Role role) { + this.role = role; + } + + public Organisation getOrganisation() { + return organisation; + } + public void setOrganisation(Organisation organisation) { + this.organisation = organisation; + } + } diff --git a/app/src/main/java/lu/circl/mispbump/models/restModels/Organisation.java b/app/src/main/java/lu/circl/mispbump/models/restModels/Organisation.java index 946efaa..b403309 100644 --- a/app/src/main/java/lu/circl/mispbump/models/restModels/Organisation.java +++ b/app/src/main/java/lu/circl/mispbump/models/restModels/Organisation.java @@ -1,6 +1,11 @@ package lu.circl.mispbump.models.restModels; +import androidx.annotation.NonNull; + +import java.util.Arrays; + + /** * Information gathered from Misp API about a organisation. */ @@ -21,27 +26,14 @@ public class Organisation { private String created_by; private Integer user_count; - public Organisation() { - } - - public Organisation(String name) { - this.name = name; - } - - public Organisation(String name, String description) { - this.name = name; - this.description = description; - } public Organisation toSyncOrganisation() { Organisation organisation = new Organisation(); - organisation.local = true; organisation.name = name; organisation.uuid = uuid; organisation.description = description; organisation.nationality = nationality; organisation.sector = sector; - organisation.type = "Sync organisation"; organisation.contacts = contacts; return organisation; @@ -64,19 +56,19 @@ public class Organisation { this.name = name; } - public String getDate_created() { + public String getDateCreated() { return date_created; } - public void setDate_created(String date_created) { + public void setDateCreated(String date_created) { this.date_created = date_created; } - public String getDate_modified() { + public String getDateModified() { return date_modified; } - public void setDate_modified(String date_modified) { + public void setDateModified(String date_modified) { this.date_modified = date_modified; } @@ -136,47 +128,47 @@ public class Organisation { this.uuid = uuid; } - public String[] getRestricted_to_domain() { + public String[] getRestrictedToDomain() { return restricted_to_domain; } - public void setRestricted_to_domain(String[] restricted_to_domain) { + public void setRestrictedToDomain(String[] restricted_to_domain) { this.restricted_to_domain = restricted_to_domain; } - public String getCreated_by() { + public String getCreatedBy() { return created_by; } - public void setCreated_by(String created_by) { + public void setCreatedBy(String created_by) { this.created_by = created_by; } - public Integer getUser_count() { + public Integer getUserCount() { return user_count; } - public void setUser_count(Integer user_count) { + public void setUserCount(Integer user_count) { this.user_count = user_count; } + @NonNull @Override public String toString() { - return "Organisation{" + - "id=" + id + - ", name='" + name + '\'' + - ", date_created='" + date_created + '\'' + - ", date_modified='" + date_modified + '\'' + - ", type='" + type + '\'' + - ", nationality='" + nationality + '\'' + - ", sector='" + sector + '\'' + - ", contacts='" + contacts + '\'' + - ", description='" + description + '\'' + - ", local=" + local + - ", uuid='" + uuid + '\'' + - ", restricted_to_domain='" + restricted_to_domain + '\'' + - ", created_by='" + created_by + '\'' + - ", user_count=" + user_count + - '}'; + return "Organisation: \n" + + "\t id = " + id + "\n" + + "\t name = " + name + '\n' + + "\t date_created = " + date_created + '\n' + + "\t date_modified = " + date_modified + '\n' + + "\t type = " + type + '\n' + + "\t nationality = " + nationality + '\n' + + "\t sector = " + sector + '\n' + + "\t contacts = " + contacts + '\n' + + "\t description = " + description + '\n' + + "\t local = " + local + '\n' + + "\t uuid = " + uuid + '\n' + + "\t restricted_to_domain = " + Arrays.toString(restricted_to_domain) + '\n' + + "\t created_by = " + created_by + '\n' + + "\t user_count = " + user_count; } } diff --git a/app/src/main/java/lu/circl/mispbump/models/restModels/Role.java b/app/src/main/java/lu/circl/mispbump/models/restModels/Role.java index 3c29a66..95c50f9 100644 --- a/app/src/main/java/lu/circl/mispbump/models/restModels/Role.java +++ b/app/src/main/java/lu/circl/mispbump/models/restModels/Role.java @@ -64,6 +64,13 @@ public class Role { @SerializedName("permission_description") private String permissionDescription; + + public boolean isSyncUserRole() { + return permSync && permAuth && permTagger && permTagEditor && permSharingGroup + && permDelegate && permSighting && permPublishZmq && permPublishKafka; + } + + public Integer getId() { return id; } diff --git a/app/src/main/java/lu/circl/mispbump/models/restModels/Server.java b/app/src/main/java/lu/circl/mispbump/models/restModels/Server.java index 33b7e0d..a8f07f0 100644 --- a/app/src/main/java/lu/circl/mispbump/models/restModels/Server.java +++ b/app/src/main/java/lu/circl/mispbump/models/restModels/Server.java @@ -1,112 +1,241 @@ package lu.circl.mispbump.models.restModels; -import com.google.gson.annotations.SerializedName; +import androidx.annotation.NonNull; public class Server { - public Server() { + private Integer id; + private String name; + private String url; + private String authkey; + private Integer org_id; + private Boolean push = false; + private Boolean pull = false; + private Object lastpulledid; + private Object lastpushedid; + private Object organization; + private Integer remote_org_id; + private Boolean publish_without_email = false; + private Boolean unpublish_event = false; + private Boolean self_signed = false; + private String pull_rules; + private String push_rules; + private Object cert_file; + private Object client_cert_file; + private Boolean internal; + private Boolean skip_proxy; + private Boolean caching_enabled = false; + private Boolean cache_timestamp; + + + public Server(String url) { + this.url = url; } - public Server(String name, String url, String authkey, Integer remote_org_id) { + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { this.url = url; + } + + public String getAuthkey() { + return authkey; + } + + public void setAuthkey(String authkey) { this.authkey = authkey; + } + + public Integer getOrgId() { + return org_id; + } + + public void setOrgId(Integer org_id) { + this.org_id = org_id; + } + + public Boolean getPush() { + return push; + } + + public void setPush(Boolean push) { + this.push = push; + } + + public Boolean getPull() { + return pull; + } + + public void setPull(Boolean pull) { + this.pull = pull; + } + + public Object getLastpulledId() { + return lastpulledid; + } + + public void setLastpulledId(Object lastpulledid) { + this.lastpulledid = lastpulledid; + } + + public Object getLastpushedId() { + return lastpushedid; + } + + public void setLastpushedId(Object lastpushedid) { + this.lastpushedid = lastpushedid; + } + + public Object getOrganization() { + return organization; + } + + public void setOrganization(Object organization) { + this.organization = organization; + } + + public Integer getRemoteOrgId() { + return remote_org_id; + } + + public void setRemoteOrgId(Integer remote_org_id) { this.remote_org_id = remote_org_id; } - @SerializedName("id") - public Integer id; + public Boolean getPublishWithoutEmail() { + return publish_without_email; + } - @SerializedName("name") - public String name; + public void setPublishWithoutEmail(Boolean publish_without_email) { + this.publish_without_email = publish_without_email; + } - @SerializedName("url") - public String url; + public Boolean getUnpublishEvent() { + return unpublish_event; + } - @SerializedName("authkey") - public String authkey; + public void setUnpublishEvent(Boolean unpublish_event) { + this.unpublish_event = unpublish_event; + } - @SerializedName("org_id") - public Integer org_id; + public Boolean getSelfSigned() { + return self_signed; + } - @SerializedName("push") - public Boolean push; + public void setSelfSigned(Boolean self_signed) { + this.self_signed = self_signed; + } - @SerializedName("pull") - public Boolean pull; + public String getPullRules() { + return pull_rules; + } - @SerializedName("lastpulledid") - public Object lastpulledid; + public void setPullRules(String pull_rules) { + this.pull_rules = pull_rules; + } - @SerializedName("lastpushedid") - public Object lastpushedid; + public String getPushRules() { + return push_rules; + } - @SerializedName("organization") - public Object organization; + public void setPushRules(String push_rules) { + this.push_rules = push_rules; + } - @SerializedName("remote_org_id") - public Integer remote_org_id; + public Object getCertFile() { + return cert_file; + } - @SerializedName("publish_without_email") - public Boolean publish_without_email = false; + public void setCertFile(Object cert_file) { + this.cert_file = cert_file; + } - @SerializedName("unpublish_event") - public Boolean unpublish_event; + public Object getClientCertFile() { + return client_cert_file; + } - @SerializedName("self_signed") - public Boolean self_signed = false; + public void setClientCertFile(Object client_cert_file) { + this.client_cert_file = client_cert_file; + } - @SerializedName("pull_rules") - public String pull_rules; + public Boolean getInternal() { + return internal; + } - @SerializedName("push_rules") - public String push_rules; + public void setInternal(Boolean internal) { + this.internal = internal; + } - @SerializedName("cert_file") - public Object cert_file; + public Boolean getSkipProxy() { + return skip_proxy; + } - @SerializedName("client_cert_file") - public Object client_cert_file; + public void setSkipProxy(Boolean skip_proxy) { + this.skip_proxy = skip_proxy; + } - @SerializedName("internal") - public Boolean internal; + public Boolean getCachingEnabled() { + return caching_enabled; + } - @SerializedName("skip_proxy") - public Boolean skip_proxy = false; + public void setCachingEnabled(Boolean caching_enabled) { + this.caching_enabled = caching_enabled; + } - @SerializedName("caching_enabled") - public Boolean caching_enabled; + public Boolean getCacheTimestamp() { + return cache_timestamp; + } - @SerializedName("cache_timestamp") - public Boolean cache_timestamp; + public void setCacheTimestamp(Boolean cache_timestamp) { + this.cache_timestamp = cache_timestamp; + } + @NonNull @Override public String toString() { - return "Server{" + - "id=" + id + - ", name='" + name + '\'' + - ", url='" + url + '\'' + - ", authkey='" + authkey + '\'' + - ", org_id=" + org_id + - ", push=" + push + - ", pull=" + pull + - ", lastpulledid=" + lastpulledid + - ", lastpushedid=" + lastpushedid + - ", organization=" + organization + - ", remote_org_id=" + remote_org_id + - ", publish_without_email=" + publish_without_email + - ", unpublish_event=" + unpublish_event + - ", self_signed=" + self_signed + - ", pull_rules='" + pull_rules + '\'' + - ", push_rules='" + push_rules + '\'' + - ", cert_file=" + cert_file + - ", client_cert_file=" + client_cert_file + - ", internal=" + internal + - ", skip_proxy=" + skip_proxy + - ", caching_enabled=" + caching_enabled + - ", cache_timestamp=" + cache_timestamp + - '}'; + return "Server: \n" + + "\t id = " + id + '\n' + + "\t name = " + name + '\n' + + "\t url = " + url + '\n' + + "\t authkey = " + authkey + '\n' + + "\t org_id = " + org_id + '\n' + + "\t push = " + push + '\n' + + "\t pull = " + pull + '\n' + + "\t lastpulledid = " + lastpulledid + '\n' + + "\t lastpushedid = " + lastpushedid + '\n' + + "\t organization = " + organization + '\n' + + "\t remote_org_id = " + remote_org_id + '\n' + + "\t publish_without_email = " + publish_without_email + '\n' + + "\t unpublish_event = " + unpublish_event + '\n' + + "\t self_signed = " + self_signed + '\n' + + "\t pull_rules = " + pull_rules + '\n' + + "\t push_rules = " + push_rules + '\n' + + "\t cert_file = " + cert_file + '\n' + + "\t client_cert_file = " + client_cert_file + '\n' + + "\t internal = " + internal + '\n' + + "\t skip_proxy = " + skip_proxy + '\n' + + "\t caching_enabled = " + caching_enabled + '\n' + + "\t cache_timestamp = " + cache_timestamp; } } diff --git a/app/src/main/java/lu/circl/mispbump/models/restModels/User.java b/app/src/main/java/lu/circl/mispbump/models/restModels/User.java index b9965e8..9ba4807 100644 --- a/app/src/main/java/lu/circl/mispbump/models/restModels/User.java +++ b/app/src/main/java/lu/circl/mispbump/models/restModels/User.java @@ -1,127 +1,249 @@ package lu.circl.mispbump.models.restModels; -import com.google.gson.annotations.Expose; -import com.google.gson.annotations.SerializedName; +import androidx.annotation.NonNull; + +import lu.circl.mispbump.auxiliary.RandomString; public class User { - public static final int ROLE_ADMIN = 1; - public static final int ROLE_ORG_ADMIN = 2; - public static final int ROLE_USER = 3; - public static final int ROLE_PUBLISHER = 4; - public static final int ROLE_SYNC_USER = 5; - public static final int ROLE_READ_ONLY = 6; + private Integer id; + private String password; + private Integer org_id; + private String email; + private Boolean autoalert; + private String authkey; + private String invited_by; + private Object gpgkey; + private String certif_public; + private String nids_sid; + private Boolean termsaccepted; + private String newsread; + private Integer role_id; + private String change_pw; + private Boolean contactalert; + private Boolean disabled; + private Object expiration; + private String current_login; + private String last_login; + private Boolean force_logout; + private Object date_created; + private String date_modified; - public User() { + + public User toSyncUser() { + User user = new User(); + user.email = email; + user.authkey = new RandomString(40).nextString(); + user.password = new RandomString(16).nextString(); + + return user; } - public User(Integer org_id, String email, Integer role_id) { - this.org_id = org_id; - this.email = email; - this.role_id = role_id; + + public Integer getId() { + return id; } - public User(Integer org_id, String email, Integer role_id, String password) { + public void setId(Integer id) { + this.id = id; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { this.password = password; + } + + public Integer getOrgId() { + return org_id; + } + + public void setOrgId(Integer org_id) { this.org_id = org_id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { this.email = email; + } + + public Boolean getAutoalert() { + return autoalert; + } + + public void setAutoalert(Boolean autoalert) { + this.autoalert = autoalert; + } + + public String getAuthkey() { + return authkey; + } + + public void setAuthkey(String authkey) { + this.authkey = authkey; + } + + public String getInvitedBy() { + return invited_by; + } + + public void setInvitedBy(String invited_by) { + this.invited_by = invited_by; + } + + public Object getGpgKey() { + return gpgkey; + } + + public void setGpgKey(Object gpgkey) { + this.gpgkey = gpgkey; + } + + public String getCertIfPublic() { + return certif_public; + } + + public void setCertIfPublic(String certif_public) { + this.certif_public = certif_public; + } + + public String getNidsSid() { + return nids_sid; + } + + public void setNidsSid(String nids_sid) { + this.nids_sid = nids_sid; + } + + public Boolean getTermsAccepted() { + return termsaccepted; + } + + public void setTermsAccepted(Boolean termsaccepted) { + this.termsaccepted = termsaccepted; + } + + public String getNewsRead() { + return newsread; + } + + public void setNewsRead(String newsread) { + this.newsread = newsread; + } + + public Integer getRoleId() { + return role_id; + } + + public void setRoleId(Integer role_id) { this.role_id = role_id; } - @SerializedName("id") - @Expose - public Integer id; - @SerializedName("password") - @Expose - public String password; - @SerializedName("org_id") - @Expose - public Integer org_id; - @SerializedName("email") - @Expose - public String email; - @SerializedName("autoalert") - @Expose - public Boolean autoalert; - @SerializedName("authkey") - @Expose - public String authkey; - @SerializedName("invited_by") - @Expose - public String invited_by; - @SerializedName("gpgkey") - @Expose - public Object gpgkey; - @SerializedName("certif_public") - @Expose - public String certif_public; - @SerializedName("nids_sid") - @Expose - public String nids_sid; - @SerializedName("termsaccepted") - @Expose - public Boolean termsaccepted; - @SerializedName("newsread") - @Expose - public String newsread; - @SerializedName("role_id") - @Expose - public Integer role_id; - @SerializedName("change_pw") - @Expose - public String change_pw; - @SerializedName("contactalert") - @Expose - public Boolean contactalert; - @SerializedName("disabled") - @Expose - public Boolean disabled; - @SerializedName("expiration") - @Expose - public Object expiration; - @SerializedName("current_login") - @Expose - public String current_login; - @SerializedName("last_login") - @Expose - public String last_login; - @SerializedName("force_logout") - @Expose - public Boolean force_logout; - @SerializedName("date_created") - @Expose - public Object date_created; - @SerializedName("date_modified") - @Expose - public String date_modified; + public String getChangePw() { + return change_pw; + } + public void setChangePw(String change_pw) { + this.change_pw = change_pw; + } + + public Boolean getContactalert() { + return contactalert; + } + + public void setContactalert(Boolean contactalert) { + this.contactalert = contactalert; + } + + public Boolean getDisabled() { + return disabled; + } + + public void setDisabled(Boolean disabled) { + this.disabled = disabled; + } + + public Object getExpiration() { + return expiration; + } + + public void setExpiration(Object expiration) { + this.expiration = expiration; + } + + public String getCurrentLogin() { + return current_login; + } + + public void setCurrentLogin(String current_login) { + this.current_login = current_login; + } + + public String getLastLogin() { + return last_login; + } + + public void setLastLogin(String last_login) { + this.last_login = last_login; + } + + public Boolean getForceLogout() { + return force_logout; + } + + public void setForceLogout(Boolean force_logout) { + this.force_logout = force_logout; + } + + public Object getDateCreated() { + return date_created; + } + + public void setDateCreated(Object date_created) { + this.date_created = date_created; + } + + public String getDateModified() { + return date_modified; + } + + public void setDateModified(String date_modified) { + this.date_modified = date_modified; + } + + + @NonNull @Override public String toString() { - return "User{" + - "id='" + id + '\'' + - ", password='" + password + '\'' + - ", org_id='" + org_id + '\'' + - ", email='" + email + '\'' + - ", autoalert=" + autoalert + - ", authkey='" + authkey + '\'' + - ", invited_by='" + invited_by + '\'' + - ", gpgkey=" + gpgkey + - ", certif_public='" + certif_public + '\'' + - ", nids_sid='" + nids_sid + '\'' + - ", termsaccepted=" + termsaccepted + - ", newsread='" + newsread + '\'' + - ", role_id='" + role_id + '\'' + - ", change_pw='" + change_pw + '\'' + - ", contactalert=" + contactalert + - ", disabled=" + disabled + - ", expiration=" + expiration + - ", current_login='" + current_login + '\'' + - ", last_login='" + last_login + '\'' + - ", force_logout=" + force_logout + - ", date_created=" + date_created + - ", date_modified='" + date_modified + '\'' + - '}'; + return "User: \n" + + "\t id = " + id + '\n' + + "\t password = " + password + '\n' + + "\t org_id = " + org_id + '\n' + + "\t email = " + email + '\n' + + "\t autoalert = " + autoalert + '\n' + + "\t authkey = " + authkey + '\n' + + "\t invited_by = " + invited_by + '\n' + + "\t gpgkey = " + gpgkey + '\n' + + "\t certif_public = " + certif_public + '\n' + + "\t nids_sid = " + nids_sid + '\n' + + "\t termsaccepted = " + termsaccepted + '\n' + + "\t newsread = " + newsread + '\n' + + "\t role_id = " + role_id + '\n' + + "\t change_pw = " + change_pw + '\n' + + "\t contactalert = " + contactalert + '\n' + + "\t disabled = " + disabled + '\n' + + "\t expiration = " + expiration + '\n' + + "\t current_login = " + current_login + '\n' + + "\t last_login = " + last_login + '\n' + + "\t force_logout = " + force_logout + '\n' + + "\t date_created = " + date_created + '\n' + + "\t date_modified = " + date_modified; } } diff --git a/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java b/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java index 895b1e1..7ae6adf 100644 --- a/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java +++ b/app/src/main/java/lu/circl/mispbump/security/KeyStoreWrapper.java @@ -32,7 +32,7 @@ public class KeyStoreWrapper { public static final String USER_INFO_ALIAS = "ALIAS_USER_INFO"; public static final String USER_ORGANISATION_INFO_ALIAS = "ALIAS_USER_ORGANISATION_INFO"; public static final String USER_CREDENTIALS_ALIAS = "ALIAS_USER_CREDENTIALS"; - public static final String UPLOAD_INFORMATION_ALIAS = "ALIAS_UPLOAD_INFORMATION"; + public static final String SYNC_INFORMATION_ALIAS = "ALIAS_UPLOAD_INFORMATION"; private static final String KEYSTORE_PROVIDER = "AndroidKeyStore"; private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding"; diff --git a/app/src/main/res/drawable/ic_arrow_down.xml b/app/src/main/res/drawable/ic_arrow_down.xml new file mode 100644 index 0000000..fb4120e --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_cloud_download.xml b/app/src/main/res/drawable/ic_cloud_download.xml new file mode 100644 index 0000000..33dae95 --- /dev/null +++ b/app/src/main/res/drawable/ic_cloud_download.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/tooltip_background.xml b/app/src/main/res/drawable/tooltip_background.xml new file mode 100644 index 0000000..d86b1a2 --- /dev/null +++ b/app/src/main/res/drawable/tooltip_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 413cc0f..6fa8ea7 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -18,11 +18,18 @@ app:popupTheme="@style/PopupTheme"/> - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_upload.xml b/app/src/main/res/layout/activity_upload.xml index 56efb79..8ea73db 100644 --- a/app/src/main/res/layout/activity_upload.xml +++ b/app/src/main/res/layout/activity_upload.xml @@ -6,7 +6,6 @@ android:id="@+id/rootLayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:animateLayoutChanges="true" tools:context=".activities.UploadActivity"> - - - - + android:theme="@style/ToolbarTheme" + app:popupTheme="@style/PopupTheme"/> - - - - - - - - - - - - + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_upload_credentials.xml b/app/src/main/res/layout/fragment_upload_credentials.xml deleted file mode 100644 index dc2cec0..0000000 --- a/app/src/main/res/layout/fragment_upload_credentials.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index 96b3a08..0000000 --- a/app/src/main/res/layout/fragment_upload_settings.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/material_password_view.xml b/app/src/main/res/layout/material_password_view.xml index 3350692..6dbff23 100644 --- a/app/src/main/res/layout/material_password_view.xml +++ b/app/src/main/res/layout/material_password_view.xml @@ -24,25 +24,13 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"/> - - @@ -54,7 +42,7 @@ android:layout_marginEnd="8dp" android:textAppearance="@style/AppTheme.TextAppearance.ListSubtitle" android:visibility="visible" - app:layout_constraintEnd_toStartOf="@+id/copy" + app:layout_constraintEnd_toStartOf="@+id/visibleToggle" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/material_password_title" tools:ignore="TextViewEdits" diff --git a/app/src/main/res/layout/row_upload_information.xml b/app/src/main/res/layout/row_upload_information.xml index 6c1b099..260d482 100644 --- a/app/src/main/res/layout/row_upload_information.xml +++ b/app/src/main/res/layout/row_upload_information.xml @@ -7,54 +7,65 @@ android:id="@+id/rootLayout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/white" - android:foreground="?attr/selectableItemBackgroundBorderless" - android:layout_marginBottom="1dp" - android:padding="8dp"> + android:foreground="?attr/selectableItemBackgroundBorderless"> - + app:layout_constraintStart_toStartOf="parent" + android:textSize="14sp" + tools:text="Aug"/> + + - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + diff --git a/app/src/main/res/layout/view_upload_action.xml b/app/src/main/res/layout/view_upload_action.xml index 5636654..0796856 100644 --- a/app/src/main/res/layout/view_upload_action.xml +++ b/app/src/main/res/layout/view_upload_action.xml @@ -10,7 +10,7 @@ android:animateLayoutChanges="true"> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0a4a7b5..d936fef 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -49,4 +49,5 @@ Allgemein Besuchen Sie das Github Projekt App Informationen + Synchronisations Details diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 5e0218a..7175677 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -21,8 +21,11 @@ - - + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8d311f4..fa4e0ad 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -17,6 +17,10 @@ #F0F0F0 #000 + #B3000000 + #80000000 + #4D000000 + #1A000000 #4CAF50 #FB8C00 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 55826cb..b49bdc6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,4 +55,5 @@ General App information Visit the Github project + Synchronisation details diff --git a/app/src/test/java/lu/circl/mispbump/ExampleUnitTest.java b/app/src/test/java/lu/circl/mispbump/ExampleUnitTest.java index 9dbb454..ac2283d 100644 --- a/app/src/test/java/lu/circl/mispbump/ExampleUnitTest.java +++ b/app/src/test/java/lu/circl/mispbump/ExampleUnitTest.java @@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals; */ public class ExampleUnitTest { @Test - public void addition_isCorrect() { + public void additionIsCorrect() { assertEquals(4, 2 + 2); } } diff --git a/build.gradle b/build.gradle index 76d59b3..02d253b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.31' + ext.kotlin_version = '1.3.41' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:3.5.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/expandablecardview/.gitignore b/expandablecardview/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/expandablecardview/.gitignore @@ -0,0 +1 @@ +/build diff --git a/expandablecardview/build.gradle b/expandablecardview/build.gradle new file mode 100644 index 0000000..8f1186a --- /dev/null +++ b/expandablecardview/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.1" + + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'androidx.appcompat:appcompat:1.0.2' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/expandablecardview/proguard-rules.pro b/expandablecardview/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/expandablecardview/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/expandablecardview/src/androidTest/java/lu/circl/expandablecardview/ExampleInstrumentedTest.java b/expandablecardview/src/androidTest/java/lu/circl/expandablecardview/ExampleInstrumentedTest.java new file mode 100644 index 0000000..5363ad2 --- /dev/null +++ b/expandablecardview/src/androidTest/java/lu/circl/expandablecardview/ExampleInstrumentedTest.java @@ -0,0 +1,29 @@ +package lu.circl.expandablecardview; + + +import android.content.Context; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; + + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("lu.circl.expandablecardview.test", appContext.getPackageName()); + } +} diff --git a/expandablecardview/src/main/AndroidManifest.xml b/expandablecardview/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29832b5 --- /dev/null +++ b/expandablecardview/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/expandablecardview/src/main/java/lu/circl/expandablecardview/ExpandableCardView.java b/expandablecardview/src/main/java/lu/circl/expandablecardview/ExpandableCardView.java new file mode 100644 index 0000000..6f4ed2f --- /dev/null +++ b/expandablecardview/src/main/java/lu/circl/expandablecardview/ExpandableCardView.java @@ -0,0 +1,177 @@ +package lu.circl.expandablecardview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.GradientDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.Transformation; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + + +public class ExpandableCardView extends LinearLayout { + + private Context context; + + private FrameLayout contentLayout; + private int cardContentPadding; + + private boolean isExpanded = true; + private int animationSpeed = 200; + + + public ExpandableCardView(Context context) { + this(context, null); + } + + public ExpandableCardView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ExpandableCardView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.context = context; + + setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + setOrientation(VERTICAL); + setClipToOutline(true); + + TypedArray customAttributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ExpandableCardView, defStyleAttr, 0); + + // general + int cornerRadius = customAttributes.getDimensionPixelSize(R.styleable.ExpandableCardView_card_corner_radius, 12); + + // header + String cardTitle = customAttributes.getString(R.styleable.ExpandableCardView_card_title); + int iconRes = customAttributes.getResourceId(R.styleable.ExpandableCardView_card_icon, 0x0); + int headerForegroundColor = customAttributes.getColor(R.styleable.ExpandableCardView_card_header_foreground_color, 0xFF000000); + int headerBackgroundColor = customAttributes.getColor(R.styleable.ExpandableCardView_card_header_background_color, 0xFFFFFFFF); + + // content + cardContentPadding = customAttributes.getDimensionPixelSize(R.styleable.ExpandableCardView_card_content_padding, 0); + int cardContentBackgroundColor = customAttributes.getColor(R.styleable.ExpandableCardView_card_content_background_color, 0xFFFFFFFF); + + customAttributes.recycle(); + + GradientDrawable cardBackground = new GradientDrawable(); + cardBackground.setCornerRadius(cornerRadius); + cardBackground.setColor(cardContentBackgroundColor); + + setBackground(cardBackground); + setElevation(10); + + initHeader(cardTitle, iconRes, headerBackgroundColor, headerForegroundColor); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (getChildCount() == 0) { + super.addView(child, index, params); // add header + } else { + if (contentLayout == null) { + contentLayout = new FrameLayout(context); + contentLayout.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + contentLayout.setPadding(cardContentPadding, cardContentPadding, cardContentPadding, cardContentPadding); + + super.addView(contentLayout, index, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + } + + contentLayout.addView(child); + } + } + + + private void initHeader(String title, int iconRes, int backgroundColor, int foregroundColor) { + View header = LayoutInflater.from(context).inflate(R.layout.expandable_card_view_header, this, true); + LinearLayout ll = header.findViewById(R.id.llRoot); + ll.setBackgroundColor(backgroundColor); + + TextView titleTextView = header.findViewById(R.id.expandable_card_view_header_title); + titleTextView.setText(title); + titleTextView.setTextColor(foregroundColor); + + ImageView iconView = header.findViewById(R.id.expandable_card_view_header_icon); + if (iconRes == 0x0) { + iconView.setVisibility(GONE); + } else { + iconView.setImageResource(iconRes); + iconView.setColorFilter(foregroundColor); + } + + final ImageButton expandToggle = header.findViewById(R.id.expandable_card_view_header_toggle); + expandToggle.setColorFilter(foregroundColor); + expandToggle.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + if (isExpanded) { + collapse(contentLayout); + expandToggle.animate().rotation(0).setDuration(animationSpeed); + } else { + expand(contentLayout); + expandToggle.animate().rotation(180).setDuration(animationSpeed); + } + + isExpanded = !isExpanded; + } + }); + } + + private void expand(final View v) { + int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY); + int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + v.measure(matchParentMeasureSpec, wrapContentMeasureSpec); + final int targetHeight = v.getMeasuredHeight(); + + v.getLayoutParams().height = 1; + v.setVisibility(View.VISIBLE); + Animation a = new Animation() { + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + v.getLayoutParams().height = interpolatedTime == 1 + ? LayoutParams.WRAP_CONTENT + : (int) (targetHeight * interpolatedTime); + v.requestLayout(); + } + + @Override + public boolean willChangeBounds() { + return true; + } + }; + + a.setDuration(animationSpeed); + v.startAnimation(a); + } + + private void collapse(final View v) { + final int initialHeight = v.getMeasuredHeight(); + + Animation a = new Animation() { + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + if (interpolatedTime == 1) { + v.setVisibility(View.GONE); + } else { + v.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime); + v.requestLayout(); + } + } + + @Override + public boolean willChangeBounds() { + return true; + } + }; + + a.setDuration(animationSpeed); + v.startAnimation(a); + } + +} diff --git a/expandablecardview/src/main/res/drawable/ic_info_outline.xml b/expandablecardview/src/main/res/drawable/ic_info_outline.xml new file mode 100644 index 0000000..cf53e14 --- /dev/null +++ b/expandablecardview/src/main/res/drawable/ic_info_outline.xml @@ -0,0 +1,9 @@ + + + diff --git a/expandablecardview/src/main/res/drawable/ic_keyboard_arrow_down.xml b/expandablecardview/src/main/res/drawable/ic_keyboard_arrow_down.xml new file mode 100644 index 0000000..ad33063 --- /dev/null +++ b/expandablecardview/src/main/res/drawable/ic_keyboard_arrow_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/expandablecardview/src/main/res/layout/expandable_card_view_header.xml b/expandablecardview/src/main/res/layout/expandable_card_view_header.xml new file mode 100644 index 0000000..53ce5da --- /dev/null +++ b/expandablecardview/src/main/res/layout/expandable_card_view_header.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/expandablecardview/src/main/res/values/attrs.xml b/expandablecardview/src/main/res/values/attrs.xml new file mode 100644 index 0000000..78a2ccc --- /dev/null +++ b/expandablecardview/src/main/res/values/attrs.xml @@ -0,0 +1,17 @@ + + + + // general + + + // header + + + + + + // content + + + + diff --git a/expandablecardview/src/main/res/values/strings.xml b/expandablecardview/src/main/res/values/strings.xml new file mode 100644 index 0000000..f771913 --- /dev/null +++ b/expandablecardview/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + ExpandableCardView + diff --git a/expandablecardview/src/test/java/lu/circl/expandablecardview/ExampleUnitTest.java b/expandablecardview/src/test/java/lu/circl/expandablecardview/ExampleUnitTest.java new file mode 100644 index 0000000..d16f0c4 --- /dev/null +++ b/expandablecardview/src/test/java/lu/circl/expandablecardview/ExampleUnitTest.java @@ -0,0 +1,19 @@ +package lu.circl.expandablecardview; + + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4e2d414..faf19fb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed May 08 12:00:08 CEST 2019 +#Wed Aug 21 13:50:22 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/settings.gradle b/settings.gradle index e7b4def..ff5c4e6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app' +include ':app', ':expandablecardview'