From 612615ff028eff3368ff9079c96c37403dda0400 Mon Sep 17 00:00:00 2001 From: Felix Prahl-Kamps Date: Thu, 22 Aug 2019 16:58:50 +0200 Subject: [PATCH] cleanup prepare for further model change --- .idea/codeStyles/Project.xml | 116 +++++++ app/build.gradle | 18 +- app/src/main/AndroidManifest.xml | 37 ++- .../mispbump/activities/ExchangeActivity.java | 72 ++--- .../mispbump/activities/HomeActivity.java | 58 +--- ...tUpActivity.java => LauncherActivity.java} | 4 +- .../activities/NetworkTestActivity.java | 99 ++++++ .../mispbump/activities/ProfileActivity.java | 34 +-- .../activities/SyncInfoDetailActivity.java | 190 ++++++++++++ .../mispbump/activities/UploadActivity.java | 283 +++++++---------- .../activities/UploadInfoActivity.java | 269 ---------------- .../mispbump/adapters/SyncInfoAdapter.java | 99 ++++++ .../mispbump/adapters/UploadInfoAdapter.java | 98 ------ .../mispbump/auxiliary/DialogManager.java | 12 +- .../mispbump/auxiliary/MispRestClient.java | 35 ++- .../mispbump/auxiliary/PreferenceManager.java | 118 +++---- .../customViews/MaterialPasswordView.java | 31 +- .../customViews/ProgressActionView.java | 146 +++++++++ .../mispbump/customViews/UploadAction.java | 13 +- .../fragments/UploadCredentialsFragment.java | 68 ----- .../fragments/UploadSettingsFragment.java | 89 ------ ...ispRestInterface.java => MispService.java} | 2 +- .../mispbump/models/ExchangeInformation.java | 44 +++ .../mispbump/models/SyncInformation.java | 115 ++++++- .../lu/circl/mispbump/models/SyncModel.java | 91 ------ .../mispbump/models/UploadInformation.java | 130 -------- .../models/restModels/Organisation.java | 51 ++-- .../mispbump/models/restModels/Server.java | 64 ++-- .../mispbump/models/restModels/User.java | 59 ++-- .../mispbump/security/KeyStoreWrapper.java | 2 +- app/src/main/res/drawable/ic_arrow_down.xml | 9 + .../main/res/drawable/ic_cloud_download.xml | 9 + .../main/res/drawable/tooltip_background.xml | 6 + .../main/res/layout/activity_network_test.xml | 8 + .../res/layout/activity_sync_info_detail.xml | 289 ++++++++++++++++++ app/src/main/res/layout/activity_upload.xml | 139 +++++---- .../layout/fragment_upload_credentials.xml | 51 ---- .../res/layout/fragment_upload_settings.xml | 75 ----- .../res/layout/material_password_view.xml | 16 +- .../res/layout/row_upload_information.xml | 71 +++-- .../main/res/layout/view_upload_action.xml | 9 +- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/attrs.xml | 7 +- app/src/main/res/values/colors.xml | 4 + app/src/main/res/values/strings.xml | 1 + build.gradle | 4 +- expandablecardview/.gitignore | 1 + expandablecardview/build.gradle | 34 +++ expandablecardview/proguard-rules.pro | 21 ++ .../ExampleInstrumentedTest.java | 29 ++ .../src/main/AndroidManifest.xml | 1 + .../ExpandableCardView.java | 177 +++++++++++ .../src/main/res/drawable/ic_info_outline.xml | 9 + .../res/drawable/ic_keyboard_arrow_down.xml | 9 + .../layout/expandable_card_view_header.xml | 37 +++ .../src/main/res/values/attrs.xml | 17 ++ .../src/main/res/values/strings.xml | 3 + .../expandablecardview/ExampleUnitTest.java | 19 ++ gradle/wrapper/gradle-wrapper.properties | 4 +- settings.gradle | 2 +- 60 files changed, 1970 insertions(+), 1539 deletions(-) create mode 100644 .idea/codeStyles/Project.xml rename app/src/main/java/lu/circl/mispbump/activities/{StartUpActivity.java => LauncherActivity.java} (87%) create mode 100644 app/src/main/java/lu/circl/mispbump/activities/NetworkTestActivity.java create mode 100644 app/src/main/java/lu/circl/mispbump/activities/SyncInfoDetailActivity.java delete mode 100644 app/src/main/java/lu/circl/mispbump/activities/UploadInfoActivity.java create mode 100644 app/src/main/java/lu/circl/mispbump/adapters/SyncInfoAdapter.java delete mode 100644 app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java create mode 100644 app/src/main/java/lu/circl/mispbump/customViews/ProgressActionView.java delete mode 100644 app/src/main/java/lu/circl/mispbump/fragments/UploadCredentialsFragment.java delete mode 100644 app/src/main/java/lu/circl/mispbump/fragments/UploadSettingsFragment.java rename app/src/main/java/lu/circl/mispbump/interfaces/{MispRestInterface.java => MispService.java} (97%) create mode 100644 app/src/main/java/lu/circl/mispbump/models/ExchangeInformation.java delete mode 100644 app/src/main/java/lu/circl/mispbump/models/SyncModel.java delete mode 100644 app/src/main/java/lu/circl/mispbump/models/UploadInformation.java create mode 100644 app/src/main/res/drawable/ic_arrow_down.xml create mode 100644 app/src/main/res/drawable/ic_cloud_download.xml create mode 100644 app/src/main/res/drawable/tooltip_background.xml create mode 100644 app/src/main/res/layout/activity_network_test.xml create mode 100644 app/src/main/res/layout/activity_sync_info_detail.xml delete mode 100644 app/src/main/res/layout/fragment_upload_credentials.xml delete mode 100644 app/src/main/res/layout/fragment_upload_settings.xml create mode 100644 expandablecardview/.gitignore create mode 100644 expandablecardview/build.gradle create mode 100644 expandablecardview/proguard-rules.pro create mode 100644 expandablecardview/src/androidTest/java/lu/circl/expandablecardview/ExampleInstrumentedTest.java create mode 100644 expandablecardview/src/main/AndroidManifest.xml create mode 100644 expandablecardview/src/main/java/lu/circl/expandablecardview/ExpandableCardView.java create mode 100644 expandablecardview/src/main/res/drawable/ic_info_outline.xml create mode 100644 expandablecardview/src/main/res/drawable/ic_keyboard_arrow_down.xml create mode 100644 expandablecardview/src/main/res/layout/expandable_card_view_header.xml create mode 100644 expandablecardview/src/main/res/values/attrs.xml create mode 100644 expandablecardview/src/main/res/values/strings.xml create mode 100644 expandablecardview/src/test/java/lu/circl/expandablecardview/ExampleUnitTest.java 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..169716e 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 007bb8e..17a7300 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,10 @@ public class ExchangeActivity extends AppCompatActivity { initViews(); initCamera(); - uploadInformation = new UploadInformation(); publicKeyQr = generatePublicKeyBitmap(); + syncInformation = new SyncInformation(); + setSyncState(SyncState.KEY_EXCHANGE); } @@ -105,24 +105,23 @@ 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().getEmail(); - return syncInformation; + ExchangeInformation exchangeInformation = generateSyncExchangeInformation(); + syncInformation.setLocal(exchangeInformation); + return qrCodeGenerator.generateQrCode(diffieHellman.encrypt(new Gson().toJson(exchangeInformation))); } @@ -275,34 +274,9 @@ public class ExchangeActivity extends AppCompatActivity { 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); + ExchangeInformation remoteSyncInfo = new Gson().fromJson(diffieHellman.decrypt(qrData), ExchangeInformation.class); + syncInformation.populateRemoteExchangeInformation(remoteSyncInfo); + preferenceManager.addSyncInformation(syncInformation); setSyncState(SyncState.DATA_EXCHANGE_DONE); } catch (JsonSyntaxException e) { if (currentReadQrStatus == ReadQrStatus.PENDING) { @@ -340,10 +314,8 @@ public class ExchangeActivity extends AppCompatActivity { 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()); + 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 6a2ffc6..90a388b 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/HomeActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/HomeActivity.java @@ -12,7 +12,6 @@ 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; @@ -21,21 +20,18 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.List; import lu.circl.mispbump.R; -import lu.circl.mispbump.adapters.UploadInfoAdapter; -import lu.circl.mispbump.auxiliary.MispRestClient; +import lu.circl.mispbump.adapters.SyncInfoAdapter; import lu.circl.mispbump.auxiliary.PreferenceManager; import lu.circl.mispbump.interfaces.OnRecyclerItemClickListener; -import lu.circl.mispbump.models.SyncModel; -import lu.circl.mispbump.models.UploadInformation; -import lu.circl.mispbump.models.restModels.Server; +import lu.circl.mispbump.models.SyncInformation; public class HomeActivity extends AppCompatActivity { - private List uploadInformationList; + private List syncInformationList; private PreferenceManager preferenceManager; private RecyclerView recyclerView; - private UploadInfoAdapter uploadInfoAdapter; + private SyncInfoAdapter syncInfoAdapter; private TextView emptyRecyclerView; @Override @@ -90,55 +86,33 @@ public class HomeActivity extends AppCompatActivity { 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() { + syncInformationList = preferenceManager.getSyncInformationList(); - Pair credentials = preferenceManager.getUserCredentials(); - MispRestClient mispRestClient = MispRestClient.getInstance(credentials.first, credentials.second); - - mispRestClient.getAllServers(new MispRestClient.AllServersCallback() { - @Override - public void success(Server[] servers) { - SyncModel.createFromServer(mispRestClient, servers[0], new SyncModel.InitializeWithServerObject() { - @Override - public void success(SyncModel syncModel) { - Log.d("DEBUG", syncModel.toString()); - } - - @Override - public void failure(String error) { - - } - }); - } - - @Override - public void failure(String error) { - - } - }); - - uploadInformationList = preferenceManager.getUploadInformationList(); - - 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); + + for (SyncInformation si : syncInformationList) { + Log.d("DEBUG", si.toString()); + } } } private OnRecyclerItemClickListener onRecyclerItemClickListener() { return (v, index) -> { - Intent i = new Intent(HomeActivity.this, UploadInfoActivity.class); - i.putExtra(UploadInfoActivity.EXTRA_UPLOAD_INFO_UUID, uploadInformationList.get(index).getUuid()); + 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()); 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 87% 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..bc3ed30 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/StartUpActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/LauncherActivity.java @@ -12,14 +12,14 @@ 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) { super.onCreate(savedInstanceState); if (isUserLoggedIn()) { - Intent home = new Intent(this, HomeActivity.class); + Intent home = new Intent(this, NetworkTestActivity.class); startActivity(home); } else { Intent login = new Intent(this, LoginActivity.class); diff --git a/app/src/main/java/lu/circl/mispbump/activities/NetworkTestActivity.java b/app/src/main/java/lu/circl/mispbump/activities/NetworkTestActivity.java new file mode 100644 index 0000000..4921bdc --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/activities/NetworkTestActivity.java @@ -0,0 +1,99 @@ +package lu.circl.mispbump.activities; + + +import android.os.Bundle; +import android.util.Log; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.util.Pair; + +import java.util.List; + +import lu.circl.mispbump.R; +import lu.circl.mispbump.auxiliary.MispRestClient; +import lu.circl.mispbump.auxiliary.PreferenceManager; +import lu.circl.mispbump.interfaces.MispService; +import lu.circl.mispbump.models.SyncInformation; +import lu.circl.mispbump.models.restModels.MispOrganisation; +import lu.circl.mispbump.models.restModels.MispServer; +import lu.circl.mispbump.models.restModels.Organisation; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + + +public class NetworkTestActivity extends AppCompatActivity { + + private PreferenceManager preferenceManager; + private MispService service; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_network_test); + + preferenceManager = PreferenceManager.getInstance(NetworkTestActivity.this); + Pair credentials = preferenceManager.getUserCredentials(); + MispRestClient restClient = MispRestClient.getInstance(credentials.first, credentials.second); + service = restClient.getService(); + + loadAllSyncs(); + } + + private void boundSyncInfoToServer() { + List syncInformationList = preferenceManager.getSyncInformationList(); + + for (SyncInformation syncInfo : syncInformationList) { + String authkey = syncInfo.getSyncServer().getAuthkey(); + String localUUID = syncInfo.getLocal().getOrganisation().getUuid(); + String foreignUUID = syncInfo.getRemoteOrganisation().getUuid(); + + + } + } + + private void loadAllSyncs() { + Call> allServersCall = service.getAllServers(); + + allServersCall.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (!response.isSuccessful()) { + return; + } + + List allServers = response.body(); + + assert allServers != null; + + for (MispServer mispServer : allServers) { + loadOrganisation(mispServer.getRemoteOrganisation().getId()); + } + } + @Override + public void onFailure(Call> call, Throwable t) { + + } + }); + } + + private void loadOrganisation(int id) { + Call organisationCall = service.getOrganisation(id); + organisationCall.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (!response.isSuccessful()) { + return; + } + + Organisation org = response.body().organisation; + Log.d("DEBUG", org.toString()); + + } + @Override + public void onFailure(Call call, Throwable t) { + + } + }); + } +} 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 3ffb150..67d04df 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; @@ -120,13 +119,10 @@ public class ProfileActivity extends AppCompatActivity { } private View.OnClickListener onFabClicked() { - return new View.OnClickListener() { - @Override - public void onClick(View v) { - fab.setImageDrawable(fabLoadingDrawable); - fabLoadingDrawable.start(); - updateProfile(); - } + return v -> { + fab.setImageDrawable(fabLoadingDrawable); + fabLoadingDrawable.start(); + updateProfile(); }; } @@ -181,23 +177,15 @@ 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(); 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..d19fc37 --- /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.getRemoteOrganisation().getName()); + + MaterialPreferenceText uuid = findViewById(R.id.uuid); + uuid.setSubtitle(syncInformation.getRemoteOrganisation().getUuid()); + + MaterialPreferenceText sector = findViewById(R.id.sector); + sector.setSubtitle(syncInformation.getRemoteOrganisation().getSector()); + + MaterialPreferenceText description = findViewById(R.id.description); + description.setSubtitle(syncInformation.getRemoteOrganisation().getDescription()); + + // settings + + CheckBox allowSelfSigned = findViewById(R.id.checkbox_self_signed); + allowSelfSigned.setChecked(syncInformation.getSyncServer().getSelf_signed()); + allowSelfSigned.setOnCheckedChangeListener((compoundButton, b) -> { + syncInformation.getSyncServer().setSelf_signed(b); + + }); + + CheckBox push = findViewById(R.id.checkbox_push); + push.setChecked(syncInformation.getSyncServer().getPush()); + push.setOnCheckedChangeListener((compoundButton, b) -> syncInformation.getSyncServer().setPush(b)); + + CheckBox pull = findViewById(R.id.checkbox_pull); + pull.setChecked(syncInformation.getSyncServer().getPull()); + pull.setOnCheckedChangeListener((compundButton, b) -> syncInformation.getSyncServer().setPull(b)); + + CheckBox cache = findViewById(R.id.checkbox_cache); + cache.setChecked(syncInformation.getSyncServer().getCaching_enabled()); + cache.setOnCheckedChangeListener((compoundButton, b) -> syncInformation.getSyncServer().setCaching_enabled(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 b396970..c4b05f5 100644 --- a/app/src/main/java/lu/circl/mispbump/activities/UploadActivity.java +++ b/app/src/main/java/lu/circl/mispbump/activities/UploadActivity.java @@ -6,21 +6,19 @@ 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.Server; import lu.circl.mispbump.models.restModels.User; @@ -28,13 +26,74 @@ 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 View rootLayout; 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); + + rootLayout = findViewById(R.id.rootLayout); + + 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 +151,64 @@ 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(); + User syncUser = syncInformation.getSyncUser(); + syncUser.setOrg_id(organisation.getId()); -// syncUser.role_id = User.ROLE_SYNC_USER; - syncUser.setEmail(uploadInformation.getRemote().syncUserEmail); - syncUser.setPassword(uploadInformation.getRemote().syncUserPassword); - syncUser.setAuthkey(uploadInformation.getRemote().syncUserAuthkey); + syncUser.setRole_id(6); syncUser.setTermsaccepted(true); return syncUser; } private Server generateSyncServer() { - Server server = new Server(); - server.setName(uploadInformation.getRemote().organisation.getName() + "'s Sync Server"); - server.setUrl(uploadInformation.getRemote().baseUrl); - server.setRemote_org_id(uploadInformation.getRemote().organisation.getId()); - server.setAuthkey(uploadInformation.getLocal().syncUserAuthkey); - server.setPull(uploadInformation.isPull()); - server.setPush(uploadInformation.isPush()); - server.setCaching_enabled(uploadInformation.isCached()); - server.setSelf_signed(uploadInformation.isAllowSelfSigned()); + Server server = syncInformation.getSyncServer(); + server.setName(syncInformation.getRemoteOrganisation().getName() + "'s Sync Server"); + server.setRemote_org_id(syncInformation.getRemoteOrganisation().getId()); + server.setAuthkey(syncInformation.getLocal().getSyncUser().getAuthkey()); + server.setPull(syncInformation.getSyncServer().getPull()); + server.setPush(syncInformation.getSyncServer().getPush()); + server.setCaching_enabled(syncInformation.getSyncServer().getCaching_enabled()); + server.setSelf_signed(syncInformation.getSyncServer().getCaching_enabled()); 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.getRemoteOrganisation(), 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.getRemoteOrganisation().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.getRemoteOrganisation().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 +216,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) { + userAction.done("User already on MISP instance"); userAdded(user); } @Override public void failure(String error) { - setUploadActionState(userAction, UploadAction.UploadState.ERROR, error); + userAction.error(error); } }); } @@ -309,28 +248,16 @@ public class UploadActivity extends AppCompatActivity { } } - 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(); - } else { - setUploadActionState(serverAction, UploadAction.UploadState.ERROR, "Could not add server"); + serverAction.done(); + preferenceManager.addSyncInformation(syncInformation); } } } 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..5661b30 --- /dev/null +++ b/app/src/main/java/lu/circl/mispbump/adapters/SyncInfoAdapter.java @@ -0,0 +1,99 @@ +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.getRemoteOrganisation().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() { + 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 9e1594e..0000000 --- a/app/src/main/java/lu/circl/mispbump/adapters/UploadInfoAdapter.java +++ /dev/null @@ -1,98 +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 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(view -> onRecyclerPositionClickListener.onClick(view, position)); - } - - @Override - public int getItemCount() { - 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, 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..161aa51 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,7 +143,7 @@ public class DialogManager { final AlertDialog.Builder adb = new AlertDialog.Builder(context); adb.setTitle("Sync information received"); - adb.setMessage(syncInformation.organisation.getName()); + adb.setMessage(syncInformation.getRemoteOrganisation().getName()); adb.setPositiveButton("Accept", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { 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 621fc4e..d4cbfe6 100644 --- a/app/src/main/java/lu/circl/mispbump/auxiliary/MispRestClient.java +++ b/app/src/main/java/lu/circl/mispbump/auxiliary/MispRestClient.java @@ -18,7 +18,7 @@ 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; @@ -47,7 +47,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 +67,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) @@ -153,7 +158,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,7 +182,7 @@ 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) { @@ -213,7 +218,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 @@ -245,7 +250,7 @@ public class MispRestClient { */ public void getUser(int userId, final UserCallback callback) { - Call call = mispRestInterface.getUser(userId); + Call call = mispService.getUser(userId); call.enqueue(new Callback() { @Override @@ -291,7 +296,7 @@ public class MispRestClient { } public void getAllUsers(final AllUsersCallback callback) { - Call> call = mispRestInterface.getAllUsers(); + Call> call = mispService.getAllUsers(); call.enqueue(new Callback>() { @Override @@ -327,7 +332,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 @@ -357,7 +362,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 +407,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 +443,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 +471,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 @@ -494,7 +499,7 @@ public class MispRestClient { } public void getAllServers(final AllRawServersCallback callback) { - Call> call = mispRestInterface.getAllServers(); + Call> call = mispService.getAllServers(); call.enqueue(new Callback>() { @Override @@ -520,7 +525,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 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..6dada16 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"; @@ -101,21 +101,11 @@ public class PreferenceManager { */ public void setUserInfo(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(); } @@ -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 ccbf113..0c8d42f 100644 --- a/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java +++ b/app/src/main/java/lu/circl/mispbump/models/SyncInformation.java @@ -3,29 +3,118 @@ package lu.circl.mispbump.models; import androidx.annotation.NonNull; +import com.google.gson.annotations.SerializedName; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.UUID; + import lu.circl.mispbump.models.restModels.Organisation; +import lu.circl.mispbump.models.restModels.Server; +import lu.circl.mispbump.models.restModels.User; /** - * A Class that holds the information needed synchronize two misp instances. + * 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; + + @SerializedName("organisation") + private Organisation remoteOrganisation; + private User syncUser; + private Server syncServer; + 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 Organisation getRemoteOrganisation() { + return remoteOrganisation; + } + public void setRemoteOrganisation(Organisation organisation) { + this.remoteOrganisation = organisation; + } + + public User getSyncUser() { + return syncUser; + } + public void setSyncUser(User syncUser) { + this.syncUser = syncUser; + } + + public Server getSyncServer() { + return syncServer; + } + public void setSyncServer(Server syncServer) { + this.syncServer = syncServer; + } + + public ExchangeInformation getLocal() { + return local; + } + public void setLocal(ExchangeInformation local) { + this.local = local; + } + + + public void populateRemoteExchangeInformation(ExchangeInformation exchangeInformation) { + this.remoteOrganisation = exchangeInformation.getOrganisation(); + this.syncUser = exchangeInformation.getSyncUser(); + this.syncServer = exchangeInformation.getServer(); + } + @NonNull @Override public String toString() { - return "SyncInformation{" + - "organisation=" + organisation + - ", syncUserEmail='" + syncUserEmail + '\'' + - ", syncUserPassword='" + syncUserPassword + '\'' + - ", syncUserAuthkey='" + syncUserAuthkey + '\'' + - ", baseUrl='" + baseUrl + '\'' + - '}'; + return "Sync Information: \n" + + "UUID = " + uuid + "\n" + + "Date = " + getSyncDateString() + "\n" + + remoteOrganisation.toString() + "\n" + + syncUser.toString() + "\n" + + syncServer.toString() + "\n" + + local.toString(); } } diff --git a/app/src/main/java/lu/circl/mispbump/models/SyncModel.java b/app/src/main/java/lu/circl/mispbump/models/SyncModel.java deleted file mode 100644 index 97656c3..0000000 --- a/app/src/main/java/lu/circl/mispbump/models/SyncModel.java +++ /dev/null @@ -1,91 +0,0 @@ -package lu.circl.mispbump.models; - - -import androidx.annotation.NonNull; - -import java.util.UUID; - -import lu.circl.mispbump.auxiliary.MispRestClient; -import lu.circl.mispbump.models.restModels.Organisation; -import lu.circl.mispbump.models.restModels.Server; - - -public class SyncModel { - - private UUID uuid; - - private Server server; - private Organisation organisation; - private Organisation remoteOrganisation; - - - 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 static void createFromServer(MispRestClient mispRestClient, Server server, InitializeWithServerObject callback) { - SyncModel syncModel = new SyncModel(); - - syncModel.server = server; - - mispRestClient.getOrganisation(server.getOrg_id(), new MispRestClient.OrganisationCallback() { - @Override - public void success(Organisation organisation) { - syncModel.organisation = organisation; - - mispRestClient.getOrganisation(server.getRemote_org_id(), new MispRestClient.OrganisationCallback() { - @Override - public void success(Organisation organisation) { - syncModel.remoteOrganisation = organisation; - - callback.success(syncModel); - } - - @Override - public void failure(String error) { - callback.failure(error); - } - }); - } - - @Override - public void failure(String error) { - callback.failure(error); - } - }); - } - - - @NonNull - @Override - public String toString() { - return server.toString() + "\n" + organisation.toString() + "\n" + remoteOrganisation.toString(); - } - - public interface InitializeWithServerObject { - void success(SyncModel syncModel); - - void failure(String error); - } -} 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/Organisation.java b/app/src/main/java/lu/circl/mispbump/models/restModels/Organisation.java index 9eef1b0..d34febf 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 @@ -3,6 +3,8 @@ package lu.circl.mispbump.models.restModels; import androidx.annotation.NonNull; +import java.util.Arrays; + /** * Information gathered from Misp API about a organisation. @@ -20,22 +22,18 @@ public class Organisation { private String description; private Boolean local; private String uuid; -// private String[] restricted_to_domain; + private String[] restricted_to_domain; private String created_by; private Integer user_count; - public Organisation() { - } public Organisation toSyncOrganisation() { Organisation organisation = new Organisation(); - organisation.local = true; // TODO REMOVE FROME HERE! organisation.name = name; organisation.uuid = uuid; organisation.description = description; organisation.nationality = nationality; organisation.sector = sector; - organisation.type = "Sync organisation"; organisation.contacts = contacts; return organisation; @@ -130,13 +128,13 @@ public class Organisation { this.uuid = uuid; } -// public String[] getRestricted_to_domain() { -// return restricted_to_domain; -// } + public String[] getRestricted_to_domain() { + return restricted_to_domain; + } -// public void setRestricted_to_domain(String[] restricted_to_domain) { -// this.restricted_to_domain = restricted_to_domain; -// } + public void setRestricted_to_domain(String[] restricted_to_domain) { + this.restricted_to_domain = restricted_to_domain; + } public String getCreated_by() { return created_by; @@ -157,21 +155,20 @@ public class Organisation { @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='" + Arrays.toString(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/Server.java b/app/src/main/java/lu/circl/mispbump/models/restModels/Server.java index 43ae978..269bd2a 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 @@ -11,25 +11,30 @@ public class Server { private String url; private String authkey; private Integer org_id; - private Boolean push; - private Boolean pull; + 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; - private Boolean unpublish_event; - private Boolean self_signed; + 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; + private Boolean caching_enabled = false; private Boolean cache_timestamp; + public Server(String url) { + this.url = url; + } + + public Integer getId() { return id; } @@ -209,29 +214,28 @@ public class Server { @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 9c1539f..40f840c 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 @@ -3,6 +3,8 @@ package lu.circl.mispbump.models.restModels; import androidx.annotation.NonNull; +import lu.circl.mispbump.auxiliary.RandomString; + public class User { @@ -30,6 +32,16 @@ public class User { private String date_modified; + 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 Integer getId() { return id; } @@ -210,29 +222,28 @@ public class User { @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_network_test.xml b/app/src/main/res/layout/activity_network_test.xml new file mode 100644 index 0000000..a52bab7 --- /dev/null +++ b/app/src/main/res/layout/activity_network_test.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/layout/activity_sync_info_detail.xml b/app/src/main/res/layout/activity_sync_info_detail.xml new file mode 100644 index 0000000..ed6fd55 --- /dev/null +++ b/app/src/main/res/layout/activity_sync_info_detail.xml @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/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'