fix bugs
uploadInfoList not passed to each activity anymore
pull/5/head
Felix Prahl-Kamps 2019-07-02 13:34:36 +02:00
parent 48674d130a
commit be88d0e3a5
48 changed files with 1581 additions and 1221 deletions

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@ -0,0 +1,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SuspiciousNameCombination" enabled="false" level="WARNING" enabled_by_default="false">
<group names="x,width,left,right" />
<group names="y,height,top,bottom" />
<ignored>
<option name="METHOD_MATCHER_CONFIG" value="java.io.PrintStream,println,java.io.PrintWriter,println,java.lang.System,identityHashCode,java.sql.PreparedStatement,set.*,java.sql.ResultSet,update.*,java.sql.SQLOutput,write.*,java.lang.Integer,compare.*,java.lang.Long,compare.*,java.lang.Short,compare,java.lang.Byte,compare,java.lang.Character,compare,java.lang.Boolean,compare,java.lang.Math,.*,java.lang.StrictMath,.*" />
</ignored>
</inspection_tool>
</profile>
</component>

View File

@ -42,6 +42,9 @@ dependencies {
implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
implementation 'com.google.zxing:core:3.4.0'
// external
implementation 'me.saket:inboxrecyclerview:1.0.0-rc1'
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'

View File

@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="lu.circl.mispbump">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="false"
@ -15,36 +16,47 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".activities.UploadInformationActivity"
android:label="Sync details"/>
<activity
android:name=".activities.UploadActivity"
android:configChanges="orientation|screenSize"
android:label="Upload" />
<activity android:name=".activities.PreferenceActivity" />
<activity android:name=".activities.StartUpActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".activities.HomeActivity"
android:label="@string/home" />
<activity
android:name=".activities.LoginActivity"
android:label="@string/login" />
android:label="@string/login"/>
<activity
android:name=".activities.HomeActivity"
android:label="@string/app_name"/>
<activity
android:name=".activities.SyncActivity"
android:configChanges="orientation|screenSize"
android:label="@string/sync"
android:parentActivityName=".activities.HomeActivity" />
android:parentActivityName=".activities.HomeActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Translucent"/>
<activity
android:name=".activities.UploadInfoActivity"
android:parentActivityName=".activities.HomeActivity"/>
<activity
android:name=".activities.UploadActivity"
android:configChanges="orientation|screenSize"
android:label="Upload"
android:parentActivityName=".activities.HomeActivity"/>
<activity
android:name=".activities.PreferenceActivity"
android:parentActivityName=".activities.HomeActivity"/>
<activity
android:name=".activities.ProfileActivity"
android:label="Profile"
android:parentActivityName=".activities.HomeActivity"
android:theme="@style/AppTheme.Translucent" />
android:theme="@style/AppTheme.Translucent"/>
</application>
</manifest>

View File

@ -1,54 +0,0 @@
package lu.circl.mispbump;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.transition.Transition;
import android.transition.TransitionValues;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
public class RecyclerViewItemTransition extends Transition {
private static final String PROPNAME_ELEVATION = "customtransition:change_elevation:toolbar";
private void captureTransitionValues(TransitionValues transitionValues) {
transitionValues.values.put(PROPNAME_ELEVATION, transitionValues.view.getElevation());
}
@Override
public void captureStartValues(@NonNull TransitionValues transitionValues) {
captureTransitionValues(transitionValues);
}
@Override
public void captureEndValues(@NonNull TransitionValues transitionValues) {
captureTransitionValues(transitionValues);
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (null == startValues || null == endValues) {
return null;
}
final View view = endValues.view;
int startElevation = 0;
int endElevation = 6;
ValueAnimator anim = ValueAnimator.ofFloat(startElevation, endElevation);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float t = (float) animation.getAnimatedValue();
view.setElevation(t);
}
});
return anim;
}
}

View File

@ -2,12 +2,12 @@ package lu.circl.mispbump.activities;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityOptionsCompat;
@ -15,7 +15,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.gson.Gson;
import java.util.List;
@ -27,11 +26,13 @@ import lu.circl.mispbump.models.UploadInformation;
public class HomeActivity extends AppCompatActivity {
private View rootView;
private PreferenceManager preferenceManager;
public static String EXTRA_UPLOAD_INFO = "uploadInformation";
private List<UploadInformation> uploadInformationList;
private PreferenceManager preferenceManager;
private RecyclerView recyclerView;
private UploadInfoAdapter uploadInfoAdapter;
private TextView emptyRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -40,76 +41,10 @@ public class HomeActivity extends AppCompatActivity {
preferenceManager = PreferenceManager.getInstance(this);
init();
initViews();
initRecyclerView();
}
private void init() {
rootView = findViewById(R.id.rootLayout);
// populate Toolbar (Actionbar)
Toolbar myToolbar = findViewById(R.id.toolbar);
setSupportActionBar(myToolbar);
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(false);
}
FloatingActionButton sync_fab = findViewById(R.id.home_fab);
sync_fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(HomeActivity.this, SyncActivity.class));
}
});
}
private void initRecyclerView() {
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(HomeActivity.this));
uploadInfoAdapter = new UploadInfoAdapter(HomeActivity.this);
uploadInfoAdapter.setOnRecyclerItemClickListener(new OnRecyclerItemClickListener<UploadInformation>() {
@Override
public void onClick(final View v, UploadInformation item) {
Intent i = new Intent(HomeActivity.this, UploadInformationActivity.class);
i.putExtra(UploadInformationActivity.EXTRA_UPLOAD_INFO_KEY, new Gson().toJson(item));
ActivityOptionsCompat options = ActivityOptionsCompat.makeClipRevealAnimation(v.findViewById(R.id.rootLayout), (int) v.getX(), (int) v.getY(), v.getWidth(), v.getHeight());
startActivity(i, options.toBundle());
}
});
recyclerView.setAdapter(uploadInfoAdapter);
}
private void refreshRecyclerView() {
List<UploadInformation> uploadInformationList = preferenceManager.getUploadInformation();
TextView empty = findViewById(R.id.empty);
// no sync information available
if (uploadInformationList == null) {
empty.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
return;
}
// sync information available
empty.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
uploadInfoAdapter.setItems(uploadInformationList);
}
@Override
protected void onResume() {
super.onResume();
refreshRecyclerView();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
@ -131,4 +66,63 @@ public class HomeActivity extends AppCompatActivity {
// invoke superclass to handle unrecognized item (eg. homeAsUp)
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
Log.d("DEBUG", "onResume()");
refreshRecyclerView();
}
private void initViews() {
emptyRecyclerView = findViewById(R.id.empty);
Toolbar myToolbar = findViewById(R.id.toolbar);
setSupportActionBar(myToolbar);
FloatingActionButton syncFab = findViewById(R.id.home_fab);
syncFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(HomeActivity.this, SyncActivity.class));
}
});
}
private void initRecyclerView() {
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(HomeActivity.this));
uploadInfoAdapter = new UploadInfoAdapter(HomeActivity.this);
uploadInfoAdapter.setOnRecyclerPositionClickListener(onRecyclerItemClickListener());
recyclerView.setAdapter(uploadInfoAdapter);
}
private void refreshRecyclerView() {
uploadInformationList = preferenceManager.getUploadInformationList();
if (uploadInformationList.isEmpty()) {
emptyRecyclerView.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyRecyclerView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
uploadInfoAdapter.setItems(uploadInformationList);
}
}
private OnRecyclerItemClickListener<Integer> onRecyclerItemClickListener() {
return new OnRecyclerItemClickListener<Integer>() {
@Override
public void onClick(View v, Integer index) {
Intent i = new Intent(HomeActivity.this, UploadInfoActivity.class);
i.putExtra(UploadInfoActivity.EXTRA_UPLOAD_INFO_UUID, uploadInformationList.get(index).getUuid());
ActivityOptionsCompat options = ActivityOptionsCompat.makeClipRevealAnimation(v.findViewById(R.id.rootLayout), (int) v.getX(), (int) v.getY(), v.getWidth(), v.getHeight());
startActivity(i, options.toBundle());
}
};
}
}

View File

@ -7,6 +7,10 @@ import androidx.appcompat.app.AppCompatActivity;
import lu.circl.mispbump.auxiliary.PreferenceManager;
import lu.circl.mispbump.models.restModels.User;
/**
* This activity navigates to the next activity base on the user status.
* This is the first activity that gets loaded when the user starts the app.
*/
public class StartUpActivity extends AppCompatActivity {
@Override

View File

@ -1,13 +1,11 @@
package lu.circl.mispbump.activities;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.BounceInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@ -17,7 +15,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
@ -25,309 +22,308 @@ import com.google.gson.JsonSyntaxException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.List;
import lu.circl.mispbump.R;
import lu.circl.mispbump.auxiliary.DialogManager;
import lu.circl.mispbump.auxiliary.PreferenceManager;
import lu.circl.mispbump.auxiliary.QrCodeGenerator;
import lu.circl.mispbump.auxiliary.RandomString;
import lu.circl.mispbump.fragments.CameraFragment;
import lu.circl.mispbump.customViews.ExtendedBottomSheetBehavior;
import lu.circl.mispbump.fragments.SyncOptionsFragment;
import lu.circl.mispbump.fragments.UploadSettingsFragment;
import lu.circl.mispbump.models.SyncInformation;
import lu.circl.mispbump.models.UploadInformation;
import lu.circl.mispbump.security.DiffieHellman;
/**
* This class provides the sync functionality.
* It collects the necessary information, guides through the process and finally completes with
* the upload to the misp instance.
*/
public class SyncActivity extends AppCompatActivity {
// rootLayout
private CoordinatorLayout rootLayout;
private ImageView qrCodeView, bottomSheetIcon;
private TextView bottomSheetText;
private ImageButton prevButton, nextButton;
private ExtendedBottomSheetBehavior bottomSheetBehavior;
enum SyncState {
PUBLIC_KEY,
DATA
}
enum UiState {
PUBLIC_KEY_SHOW,
PUBLIC_KEY_SHOW_AND_RECEIVED,
SYNC_INFO_SHOW,
SYNC_INFO_SHOW_AND_RECEIVED
}
private SyncState currentSyncState;
// dependencies
private PreferenceManager preferenceManager;
private UploadInformation uploadInformation;
private QrCodeGenerator qrCodeGenerator;
private DiffieHellman diffieHellman;
private UploadInformation uploadInformation;
private boolean foreignPublicKeyReceived, foreignSyncInfoReceived;
// fragments
// Fragments
private CameraFragment cameraFragment;
private SyncOptionsFragment syncOptionsFragment;
// qr codes
private QrCodeGenerator qrCodeGenerator;
private Bitmap publicKeyQr, syncInfoQr;
// Views
private CoordinatorLayout rootLayout;
private FrameLayout qrFrame;
private ImageView qrCode;
private TextView qrHint;
private ImageButton prevButton, nextButton;
private SyncState currentSyncState = SyncState.settings;
private Bitmap publicKeyQrCode, syncInfoQrCode;
private enum SyncState {
settings(0),
publicKeyExchange(1),
dataExchange(2);
private final int value;
SyncState(final int value) {
this.value = value;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sync);
initializeViews();
init();
initViews();
switchState(SyncState.PUBLIC_KEY);
}
private void initializeViews() {
@Override
public void onBackPressed() {
super.onBackPressed();
switch (currentSyncState) {
case PUBLIC_KEY:
// TODO warn that sync is maybe not complete ... ?
break;
case DATA:
switchState(SyncState.PUBLIC_KEY);
break;
}
}
private void init() {
preferenceManager = PreferenceManager.getInstance(SyncActivity.this);
diffieHellman = DiffieHellman.getInstance();
qrCodeGenerator = new QrCodeGenerator(SyncActivity.this);
publicKeyQrCode = qrCodeGenerator.generateQrCode(DiffieHellman.publicKeyToString(diffieHellman.getPublicKey()));
uploadInformation = new UploadInformation();
}
private void initViews() {
rootLayout = findViewById(R.id.rootLayout);
// prev button
qrFrame = findViewById(R.id.qrFrame);
qrCode = findViewById(R.id.qrCode);
qrHint = findViewById(R.id.qrHint);
prevButton = findViewById(R.id.prevButton);
prevButton.setOnClickListener(onPrevClicked);
// next button
nextButton = findViewById(R.id.nextButton);
nextButton.setOnClickListener(onNextClicked);
// QR Code View
qrCodeView = findViewById(R.id.qrcode);
qrCodeGenerator = new QrCodeGenerator(SyncActivity.this);
bottomSheetIcon = findViewById(R.id.bottomSheetIcon);
bottomSheetText = findViewById(R.id.bottomSheetText);
diffieHellman = DiffieHellman.getInstance();
preferenceManager = PreferenceManager.getInstance(this);
View bottomSheet = findViewById(R.id.bottomSheet);
bottomSheetBehavior = (ExtendedBottomSheetBehavior) BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
bottomSheetBehavior.setSwipeable(false);
bottomSheetBehavior.setHideable(false);
publicKeyQr = generatePublicKeyQr();
switchState(SyncState.settings);
}
/**
* Called when "next button" is pressed
*/
private View.OnClickListener onNextClicked = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (currentSyncState) {
case settings:
uploadInformation = new UploadInformation();
uploadInformation.setAllowSelfSigned(syncOptionsFragment.getAllowSelfSigned());
uploadInformation.setPush(syncOptionsFragment.getPush());
uploadInformation.setPull(syncOptionsFragment.getPull());
uploadInformation.setCached(syncOptionsFragment.getCache());
private void switchState(SyncState state) {
switchFragment(state);
displayQr(state);
switchState(SyncState.publicKeyExchange);
break;
case publicKeyExchange:
switchState(SyncState.dataExchange);
break;
case dataExchange:
Intent upload = new Intent(SyncActivity.this, UploadActivity.class);
upload.putExtra(UploadActivity.EXTRA_UPLOAD_INFO, new Gson().toJson(uploadInformation));
startActivity(upload);
overridePendingTransition(R.anim.slide_in_right, android.R.anim.slide_out_right);
finish();
break;
}
switch (state) {
case PUBLIC_KEY:
if (foreignPublicKeyReceived) {
switchUiState(UiState.PUBLIC_KEY_SHOW_AND_RECEIVED);
cameraFragment.setReadQrEnabled(false);
} else {
switchUiState(UiState.PUBLIC_KEY_SHOW);
cameraFragment.setReadQrEnabled(true);
}
break;
case DATA:
if (foreignSyncInfoReceived) {
switchUiState(UiState.SYNC_INFO_SHOW_AND_RECEIVED);
cameraFragment.setReadQrEnabled(false);
} else {
switchUiState(UiState.SYNC_INFO_SHOW);
cameraFragment.setReadQrEnabled(true);
}
break;
}
};
/**
* Called when "prev button" is clicked
*/
currentSyncState = state;
}
private void switchFragment(SyncState state) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
String camTag = CameraFragment.class.getSimpleName();
String settingsTag = UploadSettingsFragment.class.getSimpleName();
switch (state) {
case PUBLIC_KEY:
case DATA:
cameraFragment = (CameraFragment) fragmentManager.findFragmentByTag(camTag);
if (cameraFragment != null) {
fragmentTransaction.show(cameraFragment);
} else {
cameraFragment = new CameraFragment();
cameraFragment.setOnQrAvailableListener(onReadQrCode);
fragmentTransaction.add(R.id.fragmentContainer, cameraFragment, camTag);
}
UploadSettingsFragment uploadSettingsFragment = (UploadSettingsFragment) fragmentManager.findFragmentByTag(settingsTag);
if (uploadSettingsFragment != null) {
fragmentTransaction.hide(uploadSettingsFragment);
}
fragmentTransaction.commit();
break;
}
}
private void displayQr(SyncState state) {
switch (state) {
case PUBLIC_KEY:
qrCode.setImageBitmap(publicKeyQrCode);
qrFrame.setVisibility(View.VISIBLE);
break;
case DATA:
qrCode.setImageBitmap(syncInfoQrCode);
qrFrame.setVisibility(View.VISIBLE);
break;
}
}
private void switchUiState(final UiState state) {
runOnUiThread(new Runnable() {
@Override
public void run() {
switch (state) {
case PUBLIC_KEY_SHOW:
prevButton.setImageDrawable(getDrawable(R.drawable.ic_close));
prevButton.setVisibility(View.VISIBLE);
nextButton.setVisibility(View.INVISIBLE);
qrReceivedFeedback(false);
break;
case PUBLIC_KEY_SHOW_AND_RECEIVED:
prevButton.setImageDrawable(getDrawable(R.drawable.ic_close));
nextButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_forward));
prevButton.setVisibility(View.VISIBLE);
nextButton.setVisibility(View.VISIBLE);
cameraFragment.disablePreview();
qrReceivedFeedback(true);
break;
case SYNC_INFO_SHOW:
prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back));
nextButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_forward));
nextButton.setVisibility(View.INVISIBLE);
prevButton.setVisibility(View.VISIBLE);
cameraFragment.enablePreview();
qrReceivedFeedback(false);
break;
case SYNC_INFO_SHOW_AND_RECEIVED:
prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back));
nextButton.setImageDrawable(getDrawable(R.drawable.ic_check));
nextButton.setVisibility(View.VISIBLE);
prevButton.setVisibility(View.VISIBLE);
cameraFragment.disablePreview();
qrReceivedFeedback(true);
break;
}
}
});
}
// listener
private View.OnClickListener onPrevClicked = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (currentSyncState) {
case settings:
case PUBLIC_KEY:
finish();
break;
case publicKeyExchange:
switchState(SyncState.settings);
break;
case dataExchange:
switchState(SyncState.publicKeyExchange);
case DATA:
switchState(SyncState.PUBLIC_KEY);
break;
}
}
};
/**
* Called when the camera fragment detects a qr code
*/
private CameraFragment.QrScanCallback onQrCodeScanned = new CameraFragment.QrScanCallback() {
private View.OnClickListener onNextClicked = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (currentSyncState) {
case PUBLIC_KEY:
switchState(SyncState.DATA);
break;
case DATA:
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.PENDING);
preferenceManager.addUploadInformation(uploadInformation);
Intent i = new Intent(SyncActivity.this, UploadInfoActivity.class);
i.putExtra(UploadInfoActivity.EXTRA_UPLOAD_INFO_UUID, uploadInformation.getUuid());
startActivity(i);
finish();
break;
}
}
};
private CameraFragment.QrScanCallback onReadQrCode = new CameraFragment.QrScanCallback() {
@Override
public void qrScanResult(String qrData) {
cameraFragment.setReadQrEnabled(false);
switch (currentSyncState) {
case publicKeyExchange:
case PUBLIC_KEY:
try {
final PublicKey pk = DiffieHellman.publicKeyFromString(qrData);
diffieHellman.setForeignPublicKey(pk);
syncInfoQr = generateSyncInfoQr();
runOnUiThread(new Runnable() {
@Override
public void run() {
nextButton.setVisibility(View.VISIBLE);
cameraFragment.disablePreview();
qrReceivedFeedback();
}
});
syncInfoQrCode = generateSyncInfoQr();
switchUiState(UiState.PUBLIC_KEY_SHOW_AND_RECEIVED);
foreignPublicKeyReceived = true;
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
Snackbar.make(rootLayout, "Invalid key", Snackbar.LENGTH_SHORT).show();
cameraFragment.setReadQrEnabled(true);
switchUiState(UiState.PUBLIC_KEY_SHOW);
}
break;
case dataExchange:
cameraFragment.setReadQrEnabled(false);
case DATA:
try {
final SyncInformation remoteSyncInfo = new Gson().fromJson(diffieHellman.decrypt(qrData), SyncInformation.class);
uploadInformation.setRemote(remoteSyncInfo);
runOnUiThread(new Runnable() {
@Override
public void run() {
cameraFragment.disablePreview();
nextButton.setVisibility(View.VISIBLE);
qrReceivedFeedback();
List<UploadInformation> uploadInformationList = preferenceManager.getUploadInformationList();
if (uploadInformationList != null) {
for (final UploadInformation ui : uploadInformationList) {
if (ui.getRemote().organisation.uuid.equals(remoteSyncInfo.organisation.uuid)) {
DialogManager.syncAlreadyExistsDialog(SyncActivity.this, new DialogManager.IDialogFeedback() {
@Override
public void positive() {
uploadInformation.setUuid(ui.getUuid());
}
@Override
public void negative() {
finish();
}
});
}
}
});
}
uploadInformation.setRemote(remoteSyncInfo);
switchUiState(UiState.SYNC_INFO_SHOW_AND_RECEIVED);
foreignSyncInfoReceived = true;
} catch (JsonSyntaxException e) {
Snackbar.make(rootLayout, "Sync information unreadable", Snackbar.LENGTH_SHORT).show();
cameraFragment.setReadQrEnabled(true);
switchUiState(UiState.SYNC_INFO_SHOW);
}
break;
}
}
};
// aux
private void switchUiState(SyncState state) {
bottomSheetIcon.setVisibility(View.INVISIBLE);
bottomSheetBehavior.setSwipeable(false);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
switch (state) {
case settings:
prevButton.setImageDrawable(getDrawable(R.drawable.ic_close));
prevButton.setVisibility(View.VISIBLE);
nextButton.setVisibility(View.VISIBLE);
hideQrCode();
break;
case publicKeyExchange:
prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back));
prevButton.setVisibility(View.VISIBLE);
nextButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_forward));
nextButton.setVisibility(View.GONE);
showQrCode(publicKeyQr);
break;
case dataExchange:
prevButton.setImageDrawable(getDrawable(R.drawable.ic_arrow_back));
prevButton.setVisibility(View.VISIBLE);
nextButton.setImageDrawable(getDrawable(R.drawable.ic_cloud_upload));
nextButton.setVisibility(View.GONE);
cameraFragment.enablePreview();
cameraFragment.setReadQrEnabled(true);
showQrCode(syncInfoQr);
break;
}
}
private void switchState(SyncState state) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
if (currentSyncState != state) {
if (state.value < currentSyncState.value) {
transaction.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
} else {
transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
}
}
currentSyncState = state;
switchUiState(currentSyncState);
switch (currentSyncState) {
case settings:
String fragTag = SyncOptionsFragment.class.getSimpleName();
syncOptionsFragment = (SyncOptionsFragment) fragmentManager.findFragmentByTag(fragTag);
if (syncOptionsFragment == null) {
syncOptionsFragment = new SyncOptionsFragment();
}
transaction.replace(R.id.sync_fragment_container, syncOptionsFragment, fragTag);
transaction.commit();
break;
case publicKeyExchange:
fragTag = CameraFragment.class.getSimpleName();
cameraFragment = (CameraFragment) fragmentManager.findFragmentByTag(fragTag);
if (cameraFragment == null) {
cameraFragment = new CameraFragment();
cameraFragment.setOnQrAvailableListener(onQrCodeScanned);
}
transaction.replace(R.id.sync_fragment_container, cameraFragment, fragTag);
transaction.commit();
break;
case dataExchange:
fragTag = CameraFragment.class.getSimpleName();
cameraFragment = (CameraFragment) fragmentManager.findFragmentByTag(fragTag);
if (cameraFragment == null) {
cameraFragment = new CameraFragment();
cameraFragment.setOnQrAvailableListener(onQrCodeScanned);
}
transaction.replace(R.id.sync_fragment_container, cameraFragment, fragTag);
transaction.commit();
break;
}
}
private Bitmap generatePublicKeyQr() {
return qrCodeGenerator.generateQrCode(DiffieHellman.publicKeyToString(diffieHellman.getPublicKey()));
}
private Bitmap generateSyncInfoQr() {
private SyncInformation generateLocalSyncInfo() {
SyncInformation syncInformation = new SyncInformation();
syncInformation.organisation = preferenceManager.getUserOrganisation().toSyncOrganisation();
syncInformation.syncUserAuthkey = new RandomString(40).nextString();
@ -335,6 +331,12 @@ public class SyncActivity extends AppCompatActivity {
syncInformation.syncUserPassword = new RandomString(16).nextString();
syncInformation.syncUserEmail = preferenceManager.getUserInfo().email;
return syncInformation;
}
private Bitmap generateSyncInfoQr() {
SyncInformation syncInformation = generateLocalSyncInfo();
uploadInformation.setLocal(syncInformation);
// encrypt serialized content
@ -344,80 +346,18 @@ public class SyncActivity extends AppCompatActivity {
return qrCodeGenerator.generateQrCode(encrypted);
}
private void showQrCode(final Bitmap bitmap) {
private void qrReceivedFeedback(final boolean done) {
runOnUiThread(new Runnable() {
@Override
public void run() {
qrCodeView.setImageBitmap(bitmap);
qrCodeView.setAlpha(0f);
qrCodeView.setVisibility(View.VISIBLE);
qrCodeView.setScaleX(0.9f);
qrCodeView.setScaleY(0.6f);
qrCodeView.animate()
.scaleX(1f)
.scaleY(1f)
.alpha(1f)
.setDuration(250)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
qrCodeView.setVisibility(View.VISIBLE);
}
});
if (done) {
qrHint.setCompoundDrawablesWithIntrinsicBounds(getDrawable(R.drawable.ic_check_outline), null, null, null);
qrHint.setCompoundDrawableTintList(ColorStateList.valueOf(getColor(R.color.status_green)));
} else {
qrHint.setCompoundDrawablesWithIntrinsicBounds(getDrawable(R.drawable.ic_info_outline), null, null, null);
qrHint.setCompoundDrawableTintList(ColorStateList.valueOf(getColor(R.color.status_amber)));
}
}
});
}
private void hideQrCode() {
if (qrCodeView.getVisibility() == View.GONE) {
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
qrCodeView.setAlpha(1f);
qrCodeView.setVisibility(View.VISIBLE);
qrCodeView.setScaleX(1f);
qrCodeView.setScaleY(1f);
qrCodeView.animate()
.scaleX(0f)
.scaleY(0f)
.alpha(0f)
.setDuration(250)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
qrCodeView.setVisibility(View.GONE);
}
});
}
});
}
private void qrReceivedFeedback() {
bottomSheetIcon.setScaleX(0f);
bottomSheetIcon.setScaleY(0f);
bottomSheetIcon.setVisibility(View.VISIBLE);
bottomSheetIcon.animate()
.scaleY(1f)
.scaleX(1f)
.setDuration(500);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
bottomSheetBehavior.setSwipeable(true);
switch (currentSyncState) {
case publicKeyExchange:
bottomSheetText.setText("Received public key from partner");
break;
case dataExchange:
bottomSheetText.setText("Received sync information from partner");
break;
}
}
}

View File

@ -1,5 +1,6 @@
package lu.circl.mispbump.activities;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
@ -9,9 +10,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.List;
@ -29,34 +28,67 @@ import lu.circl.mispbump.models.restModels.User;
public class UploadActivity extends AppCompatActivity {
public static final String EXTRA_UPLOAD_INFO = "uploadInformation";
public static String EXTRA_UPLOAD_INFO = "uploadInformation";
private PreferenceManager preferenceManager;
private MispRestClient restClient;
private UploadInformation uploadInformation;
private CoordinatorLayout rootLayout;
private MispRestClient restClient;
private UploadAction availableAction, orgAction, userAction, serverAction;
private boolean errorWhileUpload;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload);
preferenceManager = PreferenceManager.getInstance(UploadActivity.this);
restClient = MispRestClient.getInstance(this);
parseExtra();
init();
initViews();
startUpload();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
saveCurrentState();
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
super.onBackPressed();
saveCurrentState();
}
private void parseExtra() {
String uploadInfoString = getIntent().getStringExtra(EXTRA_UPLOAD_INFO);
uploadInformation = new Gson().fromJson(uploadInfoString, UploadInformation.class);
assert uploadInformation != null;
Intent i = getIntent();
UUID currentUUID = (UUID) i.getSerializableExtra(EXTRA_UPLOAD_INFO);
for (UploadInformation ui : preferenceManager.getUploadInformationList()) {
if (ui.getUuid().compareTo(currentUUID) == 0) {
uploadInformation = ui;
return;
}
}
if (uploadInformation == null) {
throw new RuntimeException("Could not find UploadInfo with UUID {" + currentUUID.toString() + "}");
}
}
private void init() {
restClient = MispRestClient.getInstance(this);
preferenceManager = PreferenceManager.getInstance(this);
private void initViews() {
rootLayout = findViewById(R.id.rootLayout);
// toolbar
@ -68,32 +100,17 @@ public class UploadActivity extends AppCompatActivity {
ab.setDisplayHomeAsUpEnabled(true);
ab.setHomeAsUpIndicator(R.drawable.ic_close);
// fab
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startUpload();
}
});
availableAction = findViewById(R.id.availableAction);
orgAction = findViewById(R.id.orgAction);
userAction = findViewById(R.id.userAction);
serverAction = findViewById(R.id.serverAction);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
preferenceManager.addUploadInformation(uploadInformation);
finish();
return true;
default:
return super.onOptionsItemSelected(item);
private void saveCurrentState() {
if (errorWhileUpload) {
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
}
preferenceManager.addUploadInformation(uploadInformation);
}
/**
@ -122,6 +139,7 @@ public class UploadActivity extends AppCompatActivity {
return syncUser;
}
private MispRestClient.AvailableCallback availableCallback = new MispRestClient.AvailableCallback() {
@Override
public void available() {
@ -136,12 +154,15 @@ public class UploadActivity extends AppCompatActivity {
availableAction.setError(error);
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
errorWhileUpload = true;
Snackbar sb = Snackbar.make(rootLayout, error, Snackbar.LENGTH_INDEFINITE);
sb.setAction("Retry", new View.OnClickListener() {
@Override
public void onClick(View v) {
availableAction.setError(null);
availableAction.setCurrentUploadState(UploadAction.UploadState.LOADING);
errorWhileUpload = false;
startUpload();
}
});
@ -165,9 +186,9 @@ public class UploadActivity extends AppCompatActivity {
public void failure(String error) {
orgAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
orgAction.setError(error);
errorWhileUpload = true;
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
preferenceManager.addUploadInformation(uploadInformation);
}
};
@ -194,9 +215,9 @@ public class UploadActivity extends AppCompatActivity {
public void failure(String error) {
userAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
userAction.setError(error);
errorWhileUpload = true;
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
preferenceManager.addUploadInformation(uploadInformation);
}
};
@ -210,17 +231,16 @@ public class UploadActivity extends AppCompatActivity {
public void success(Server server) {
serverAction.setCurrentUploadState(UploadAction.UploadState.DONE);
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.COMPLETE);
preferenceManager.addUploadInformation(uploadInformation);
finish();
saveCurrentState();
}
@Override
public void failure(String error) {
serverAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
serverAction.setError(error);
errorWhileUpload = true;
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
preferenceManager.addUploadInformation(uploadInformation);
}
};

View File

@ -0,0 +1,243 @@
package lu.circl.mispbump.activities;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import java.util.UUID;
import lu.circl.mispbump.R;
import lu.circl.mispbump.auxiliary.PreferenceManager;
import lu.circl.mispbump.fragments.UploadInfoFragment;
import lu.circl.mispbump.fragments.UploadSettingsFragment;
import lu.circl.mispbump.models.UploadInformation;
public class UploadInfoActivity extends AppCompatActivity {
public static String EXTRA_UPLOAD_INFO_UUID = "uploadInformation";
private PreferenceManager preferenceManager;
private UploadInformation uploadInformation;
private ViewPagerAdapter viewPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload_information_2);
preferenceManager = PreferenceManager.getInstance(UploadInfoActivity.this);
// tint statusBar
getWindow().setStatusBarColor(getColor(R.color.grey_light));
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
parseExtra();
initToolbar();
initViewPager();
initViews();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_upload_info, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.delete:
// TODO delete
return true;
case android.R.id.home:
saveCurrentSettings();
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onResume() {
super.onResume();
// refresh current uploadInformation
if (uploadInformation != null) {
uploadInformation = preferenceManager.getUploadInformation(uploadInformation.getUuid());
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
saveCurrentSettings();
}
private void parseExtra() {
Intent i = getIntent();
UUID currentUUID = (UUID) i.getSerializableExtra(EXTRA_UPLOAD_INFO_UUID);
uploadInformation = preferenceManager.getUploadInformation(currentUUID);
if (uploadInformation == null) {
throw new RuntimeException("Could not find UploadInformation with UUID {" + currentUUID.toString() + "}");
}
}
private void initToolbar() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
assert ab != null;
TextView tv = findViewById(R.id.syncStatus);
int statusColor;
String statusText;
Drawable statusDrawable;
switch (uploadInformation.getCurrentSyncStatus()) {
case COMPLETE:
statusColor = getColor(R.color.status_green);
statusText = "Successfully uploaded";
statusDrawable = getDrawable(R.drawable.ic_check_outline);
break;
case FAILURE:
statusColor = getColor(R.color.status_red);
statusText = "Error while uploading";
statusDrawable = getDrawable(R.drawable.ic_error_outline);
break;
case PENDING:
statusColor = getColor(R.color.status_amber);
statusText = "Not uploaded yet";
statusDrawable = getDrawable(R.drawable.ic_pending);
break;
default:
statusColor = getColor(R.color.status_green);
statusText = "Successfully uploaded";
statusDrawable = getDrawable(R.drawable.ic_check_outline);
break;
}
tv.setText(statusText);
tv.setTextColor(statusColor);
tv.setCompoundDrawablesWithIntrinsicBounds(null, null, statusDrawable, null);
tv.setCompoundDrawableTintList(ColorStateList.valueOf(statusColor));
ab.setTitle(uploadInformation.getRemote().organisation.name);
ab.setDisplayShowTitleEnabled(true);
ab.setDisplayShowCustomEnabled(false);
ab.setDisplayHomeAsUpEnabled(true);
}
private void initViewPager() {
ViewPager viewPager = findViewById(R.id.viewPager);
TabLayout tabLayout = findViewById(R.id.tabLayout);
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(), uploadInformation);
viewPager.setAdapter(viewPagerAdapter);
tabLayout.setupWithViewPager(viewPager);
}
private void initViews() {
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
saveCurrentSettings();
Intent i = new Intent(UploadInfoActivity.this, UploadActivity.class);
i.putExtra(UploadActivity.EXTRA_UPLOAD_INFO, uploadInformation.getUuid());
startActivity(i);
}
});
}
private void saveCurrentSettings() {
uploadInformation.setAllowSelfSigned(viewPagerAdapter.uploadSettingsFragment.getAllowSelfSigned());
uploadInformation.setPull(viewPagerAdapter.uploadSettingsFragment.getPull());
uploadInformation.setPush(viewPagerAdapter.uploadSettingsFragment.getPush());
uploadInformation.setCached(viewPagerAdapter.uploadSettingsFragment.getCache());
preferenceManager.addUploadInformation(uploadInformation);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private UploadSettingsFragment uploadSettingsFragment;
private UploadInfoFragment uploadInfoFragment = new UploadInfoFragment();
ViewPagerAdapter(@NonNull FragmentManager fm, UploadInformation uploadInformation) {
super(fm, ViewPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
uploadSettingsFragment = new UploadSettingsFragment(uploadInformation);
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return uploadSettingsFragment;
case 1:
return uploadInfoFragment;
default:
uploadSettingsFragment = new UploadSettingsFragment();
return uploadSettingsFragment;
}
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Permissions";
case 1:
return "Credentials";
default:
return "N/A";
}
}
@Override
public int getCount() {
return 2;
}
}
}

View File

@ -1,156 +0,0 @@
package lu.circl.mispbump.activities;
import android.animation.ValueAnimator;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.widget.ImageViewCompat;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.gson.Gson;
import lu.circl.mispbump.R;
import lu.circl.mispbump.models.UploadInformation;
public class UploadInformationActivity extends AppCompatActivity {
public static String EXTRA_UPLOAD_INFO_KEY = "uploadInformation";
private View rootLayout;
private ImageView syncStatusIcon;
private UploadInformation uploadInformation;
private FloatingActionButton fab;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload_information);
parseExtra();
init();
tintSystemBars();
populateContent();
}
private void parseExtra() {
String uploadInfo = getIntent().getStringExtra(EXTRA_UPLOAD_INFO_KEY);
this.uploadInformation = new Gson().fromJson(uploadInfo, UploadInformation.class);
}
private void init() {
rootLayout = findViewById(R.id.rootLayout);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setHomeAsUpIndicator(R.drawable.ic_close);
actionBar.setDisplayHomeAsUpEnabled(true);
fab = findViewById(R.id.fab);
syncStatusIcon = findViewById(R.id.syncStatus);
}
private void populateContent() {
switch (uploadInformation.getCurrentSyncStatus()) {
case COMPLETE:
ImageViewCompat.setImageTintList(syncStatusIcon, ColorStateList.valueOf(getColor(R.color.status_green)));
syncStatusIcon.setImageResource(R.drawable.ic_check_outline);
fab.hide();
break;
case FAILURE:
ImageViewCompat.setImageTintList(syncStatusIcon, ColorStateList.valueOf(getColor(R.color.status_red)));
syncStatusIcon.setImageResource(R.drawable.ic_error_outline);
break;
case PENDING:
ImageViewCompat.setImageTintList(syncStatusIcon, ColorStateList.valueOf(getColor(R.color.status_amber)));
syncStatusIcon.setImageResource(R.drawable.ic_info_outline);
break;
}
TextView name = findViewById(R.id.orgName);
name.setText(uploadInformation.getRemote().organisation.name);
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
fab.show();
}
@Override
public void postponeEnterTransition() {
super.postponeEnterTransition();
fab.show();
}
@Override
public void startPostponedEnterTransition() {
super.startPostponedEnterTransition();
fab.show();
}
private void tintSystemBars() {
// Initial colors of each system bar.
final int statusBarColor = getColor(R.color.white);
final int toolbarColor = getColor(R.color.white);
// Desired final colors of each bar.
final int statusBarToColor = getColor(R.color.colorPrimary);
final int toolbarToColor = getColor(R.color.colorPrimary);
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Use animation position to blend colors.
float position = animation.getAnimatedFraction();
// Apply blended color to the status bar.
int blended = blendColors(statusBarColor, statusBarToColor, position);
getWindow().setStatusBarColor(blended);
blended = blendColors(toolbarColor, toolbarToColor, position);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(blended));
}
});
anim.setDuration(500).start();
}
private int blendColors(int from, int to, float ratio) {
final float inverseRatio = 1f - ratio;
final float r = Color.red(to) * ratio + Color.red(from) * inverseRatio;
final float g = Color.green(to) * ratio + Color.green(from) * inverseRatio;
final float b = Color.blue(to) * ratio + Color.blue(from) * inverseRatio;
return Color.rgb((int) r, (int) g, (int) b);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@ -5,6 +5,8 @@ import android.content.res.ColorStateList;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.widget.ImageViewCompat;
@ -15,34 +17,30 @@ import java.util.List;
import lu.circl.mispbump.R;
import lu.circl.mispbump.interfaces.OnRecyclerItemClickListener;
import lu.circl.mispbump.models.UploadInformation;
import lu.circl.mispbump.viewholders.UploadInfoListViewHolder;
public class UploadInfoAdapter extends RecyclerView.Adapter<UploadInfoListViewHolder> {
public class UploadInfoAdapter extends RecyclerView.Adapter<UploadInfoAdapter.ViewHolder> {
private Context context;
private List<UploadInformation> items;
private OnRecyclerItemClickListener<UploadInformation> onRecyclerItemClickListener;
private OnRecyclerItemClickListener<Integer> onRecyclerPositionClickListener;
public UploadInfoAdapter(Context context) {
this.context = context;
}
public UploadInfoAdapter(Context context, List<UploadInformation> items) {
this.context = context;
this.items = items;
}
@NonNull
@Override
public UploadInfoListViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
public UploadInfoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_upload_information, viewGroup, false);
return new UploadInfoListViewHolder(v);
return new UploadInfoAdapter.ViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull final UploadInfoListViewHolder holder, int position) {
public void onBindViewHolder(@NonNull final UploadInfoAdapter.ViewHolder holder, final int position) {
final UploadInformation item = items.get(position);
@ -60,7 +58,7 @@ public class UploadInfoAdapter extends RecyclerView.Adapter<UploadInfoListViewHo
break;
case PENDING:
ImageViewCompat.setImageTintList(holder.syncStatus, ColorStateList.valueOf(context.getColor(R.color.status_amber)));
holder.syncStatus.setImageResource(R.drawable.ic_info_outline);
holder.syncStatus.setImageResource(R.drawable.ic_pending);
break;
}
@ -70,6 +68,13 @@ public class UploadInfoAdapter extends RecyclerView.Adapter<UploadInfoListViewHo
onRecyclerItemClickListener.onClick(view, item);
}
});
holder.rootView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onRecyclerPositionClickListener.onClick(view, position);
}
});
}
@Override
@ -77,12 +82,37 @@ public class UploadInfoAdapter extends RecyclerView.Adapter<UploadInfoListViewHo
return items.size();
}
public void setItems(List<UploadInformation> items) {
this.items = items;
notifyDataSetChanged();
}
// callbacks
public void setOnRecyclerItemClickListener(OnRecyclerItemClickListener<UploadInformation> onRecyclerItemClickListener) {
this.onRecyclerItemClickListener = onRecyclerItemClickListener;
}
public void setOnRecyclerPositionClickListener(OnRecyclerItemClickListener<Integer> onRecyclerPositionClickListener) {
this.onRecyclerPositionClickListener = onRecyclerPositionClickListener;
}
// viewHolder
static class ViewHolder extends RecyclerView.ViewHolder {
View rootView;
ImageView syncStatus;
TextView orgName, date;
ViewHolder(@NonNull View itemView) {
super(itemView);
rootView = itemView;
orgName = itemView.findViewById(R.id.orgName);
date = itemView.findViewById(R.id.date);
syncStatus = itemView.findViewById(R.id.syncStatus);
}
}
}

View File

@ -20,6 +20,40 @@ import lu.circl.mispbump.security.DiffieHellman;
public class DialogManager {
public static void syncAlreadyExistsDialog(Context context, final IDialogFeedback callback) {
final AlertDialog.Builder adb = new AlertDialog.Builder(context);
adb.setTitle("Sync information already exists");
adb.setMessage("You already synced with this organisation, would you like to update the information?" +
"\nUpdating the information will reset the current authkey!");
adb.setPositiveButton("Update", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (callback != null) {
callback.positive();
}
}
});
adb.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (callback != null) {
callback.negative();
}
}
});
Activity act = (Activity) context;
act.runOnUiThread(new Runnable() {
@Override
public void run() {
adb.create().show();
}
});
}
public static void saveAndExitDialog(Context context, final IDialogFeedback callback) {
final AlertDialog.Builder adb = new AlertDialog.Builder(context);

View File

@ -322,120 +322,104 @@ public class PreferenceManager {
}
public void setUploadInformationList(List<UploadInformation> uploadInformationList) {
KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS);
private List<UploadInformation> cachedUploadInformationList;
try {
String cipherText = ksw.encrypt(new Gson().toJson(uploadInformationList));
SharedPreferences.Editor editor = preferences.edit();
editor.putString(UPLOAD_INFO, cipherText);
editor.apply();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
public List<UploadInformation> getUploadInformation() {
private void loadUploadInformationList() {
KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS);
String storedUploadInfoString = preferences.getString(UPLOAD_INFO, null);
Type type = new TypeToken<List<UploadInformation>>() {}.getType();
if (storedUploadInfoString == null) {
return null;
if (storedUploadInfoString == null || storedUploadInfoString.isEmpty()) {
cachedUploadInformationList = new ArrayList<>();
} else {
try {
storedUploadInfoString = ksw.decrypt(storedUploadInfoString);
cachedUploadInformationList = new Gson().fromJson(storedUploadInfoString, type);
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
private void saveUploadInformationList() {
try {
storedUploadInfoString = ksw.decrypt(storedUploadInfoString);
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
KeyStoreWrapper ksw = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS);
String cipherText = ksw.encrypt(new Gson().toJson(cachedUploadInformationList));
SharedPreferences.Editor editor = preferences.edit();
editor.putString(UPLOAD_INFO, cipherText);
editor.apply();
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
}
return new Gson().fromJson(storedUploadInfoString, type);
public List<UploadInformation> getUploadInformationList() {
if (cachedUploadInformationList == null) {
loadUploadInformationList();
}
return cachedUploadInformationList;
}
public void setUploadInformationList(List<UploadInformation> uploadInformationList) {
cachedUploadInformationList = uploadInformationList;
saveUploadInformationList();
}
public UploadInformation getUploadInformation(UUID uuid) {
if (cachedUploadInformationList == null) {
loadUploadInformationList();
}
for (UploadInformation ui : cachedUploadInformationList) {
if (ui.getUuid().compareTo(uuid) == 0) {
return ui;
}
}
return null;
}
public void addUploadInformation(UploadInformation uploadInformation) {
List<UploadInformation> uploadInformationList = getUploadInformation();
if (uploadInformationList == null) {
uploadInformationList = new ArrayList<>();
uploadInformationList.add(uploadInformation);
setUploadInformationList(uploadInformationList);
} else {
// check if upload information already exists
for (int i = 0; i < uploadInformationList.size(); i++) {
if (uploadInformationList.get(i).getId().compareTo(uploadInformation.getId()) == 0) {
uploadInformationList.set(i, uploadInformation);
setUploadInformationList(uploadInformationList);
return;
}
}
uploadInformationList.add(uploadInformation);
setUploadInformationList(uploadInformationList);
}
}
public boolean containsUploadInformation(UUID uuid) {
List<UploadInformation> uploadInformationList = getUploadInformation();
if (uploadInformationList == null) {
return false;
if (cachedUploadInformationList == null) {
loadUploadInformationList();
}
for (UploadInformation ui : uploadInformationList) {
if (ui.getId().compareTo(uuid) == 0) {
return true;
// update if exists
for (int i = 0; i < cachedUploadInformationList.size(); i++) {
if (cachedUploadInformationList.get(i).getUuid().compareTo(uploadInformation.getUuid()) == 0) {
cachedUploadInformationList.set(i, uploadInformation);
saveUploadInformationList();
return;
}
}
return false;
// else: add
cachedUploadInformationList.add(uploadInformation);
saveUploadInformationList();
}
public boolean removeUploadInformation(UUID uuid) {
Log.d("PREFS", "uuid to delete: " + uuid.toString());
public void removeUploadInformation(UUID uuid) {
if (cachedUploadInformationList == null) {
loadUploadInformationList();
}
List<UploadInformation> uploadInformationList = getUploadInformation();
for (UploadInformation ui : uploadInformationList) {
Log.d("PREFS", "checking uuid: " + ui.getId().toString());
if (ui.getId().compareTo(uuid) == 0) {
if (uploadInformationList.size() == 1) {
for (UploadInformation ui : cachedUploadInformationList) {
if (ui.getUuid().compareTo(uuid) == 0) {
if (cachedUploadInformationList.size() == 1) {
clearUploadInformation();
} else {
uploadInformationList.remove(ui);
setUploadInformationList(uploadInformationList);
cachedUploadInformationList.remove(ui);
saveUploadInformationList();
}
return true;
}
}
return false;
}
public void clearUploadInformation() {
cachedUploadInformationList.clear();
KeyStoreWrapper keyStoreWrapper = new KeyStoreWrapper(KeyStoreWrapper.UPLOAD_INFORMATION_ALIAS);
keyStoreWrapper.deleteStoredKey();

View File

@ -22,13 +22,10 @@ public class QrCodeGenerator {
}
public Bitmap generateQrCode(String content) {
Point displaySize = new Point();
callingActivity.getWindowManager().getDefaultDisplay().getSize(displaySize);
int size = getDisplaySize().x;
int size = displaySize.x;
if (displaySize.x > displaySize.y) {
size = displaySize.y;
if (size > getDisplaySize().y) {
size = getDisplaySize().y;
}
size = (int)(size * 0.8);
@ -48,6 +45,13 @@ public class QrCodeGenerator {
return null;
}
public Point getDisplaySize() {
Point displaySize = new Point();
callingActivity.getWindowManager().getDefaultDisplay().getSize(displaySize);
return displaySize;
}
private Bitmap createBitmap(BitMatrix matrix) {
int width = matrix.getWidth();
int height = matrix.getHeight();
@ -55,7 +59,7 @@ public class QrCodeGenerator {
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = matrix.get(x, y) ? 0xFF000000 : 0x99FFFFFF;
pixels[offset + x] = matrix.get(x, y) ? 0xFF000000 : 0x00FFFFFF;
}
}

View File

@ -0,0 +1,32 @@
package lu.circl.mispbump.customViews;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;
public class ExtendedViewPager extends ViewPager {
private boolean swipeEnabled;
public ExtendedViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return swipeEnabled && super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return swipeEnabled && super.onTouchEvent(event);
}
public void setPagingEnabled(boolean enabled) {
this.swipeEnabled = enabled;
}
}

View File

@ -0,0 +1,63 @@
package lu.circl.mispbump.customViews;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Switch;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import lu.circl.mispbump.R;
public class MaterialPreferenceSwitch extends ConstraintLayout {
private View rootView;
private TextView titleView, subTitleView;
private Switch switchView;
public MaterialPreferenceSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
View view = LayoutInflater.from(context).inflate(R.layout.material_preference_switch, this);
rootView = view.findViewById(R.id.rootLayout);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MaterialPreferenceSwitch);
String title = a.getString(R.styleable.MaterialPreferenceSwitch_title);
String subTitle = a.getString(R.styleable.MaterialPreferenceSwitch_subtitle);
a.recycle();
titleView = view.findViewById(R.id.material_preference_title);
titleView.setText(title);
subTitleView = view.findViewById(R.id.material_preference_subtitle);
subTitleView.setText(subTitle);
switchView = view.findViewById(R.id.material_preference_switch);
rootView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (switchView.isEnabled()) {
switchView.setChecked(!switchView.isChecked());
}
}
});
}
public void setEnabled(boolean enabled) {
switchView.setEnabled(enabled);
}
public void setChecked(boolean checked) {
switchView.setChecked(checked);
}
public boolean isChecked() {
return switchView.isChecked();
}
}

View File

@ -11,11 +11,12 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.widget.ImageViewCompat;
import lu.circl.mispbump.R;
public class UploadAction extends LinearLayoutCompat {
public class UploadAction extends ConstraintLayout {
private Context context;
@ -26,7 +27,7 @@ public class UploadAction extends LinearLayoutCompat {
ERROR
}
private TextView errorView;
private TextView titleView, errorView;
private UploadState currentUploadState;
private ImageView stateView;
private ProgressBar progressBar;
@ -41,20 +42,23 @@ public class UploadAction extends LinearLayoutCompat {
super(context, attrs);
this.context = context;
View baseView = LayoutInflater.from(context).inflate(R.layout.view_upload_action_2, this);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UploadAction);
String title = a.getString(R.styleable.UploadAction_description);
titleView = baseView.findViewById(R.id.title);
titleView.setText(a.getString(R.styleable.UploadAction_description));
a.recycle();
LayoutInflater inflater = LayoutInflater.from(context);
View baseView = inflater.inflate(R.layout.view_upload_action, this);
errorView = baseView.findViewById(R.id.error);
stateView = baseView.findViewById(R.id.stateView);
progressBar = baseView.findViewById(R.id.progressBar);
}
errorView = findViewById(R.id.error);
TextView titleView = baseView.findViewById(R.id.title);
public void setTitle(String title) {
titleView.setText(title);
stateView = findViewById(R.id.stateView);
progressBar = findViewById(R.id.progressBar);
}
/**
@ -75,12 +79,6 @@ public class UploadAction extends LinearLayoutCompat {
progressBar.setVisibility(GONE);
switch (state) {
case PENDING:
stateView.setVisibility(VISIBLE);
stateView.setImageResource(R.drawable.ic_info_outline);
ImageViewCompat.setImageTintList(stateView, ColorStateList.valueOf(context.getColor(R.color.status_amber)));
break;
case LOADING:
stateView.setVisibility(GONE);
progressBar.setVisibility(VISIBLE);
@ -97,6 +95,12 @@ public class UploadAction extends LinearLayoutCompat {
stateView.setImageResource(R.drawable.ic_error_outline);
ImageViewCompat.setImageTintList(stateView, ColorStateList.valueOf(context.getColor(R.color.status_red)));
break;
case PENDING:
stateView.setVisibility(VISIBLE);
stateView.setImageResource(R.drawable.ic_info_outline);
ImageViewCompat.setImageTintList(stateView, ColorStateList.valueOf(context.getColor(R.color.status_amber)));
break;
}
}

View File

@ -73,10 +73,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
private int lastAccessedIndex = 0;
private Bitmap[] processQueue = new Bitmap[10];
ImageProcessingThread() {
Log.i(TAG, "Image worker thread created");
}
void addToQueue(Bitmap bitmap) {
processQueue[lastAccessedIndex] = bitmap;
// circular array access
@ -167,7 +163,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
Log.i(TAG, "Width: " + width + "; height: " + height);
openCamera(width, height);
}
@ -350,7 +345,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
} else if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, new CompareSizesByArea());
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
@ -359,8 +353,7 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_camera, container, false);
hideCamView = v.findViewById(R.id.hideCam);
hideCamView.setVisibility(View.GONE);
// hideCamView = v.findViewById(R.id.hideCam);
initRenderScript();
setUpBarcodeDetector();
@ -370,6 +363,7 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
autoFitTextureView = view.findViewById(R.id.texture);
hideCamView = view.findViewById(R.id.hideCam);
}
@Override
@ -381,20 +375,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
public void onResume() {
super.onResume();
enablePreview();
// startBackgroundThread();
//
// imageProcessingThread = new ImageProcessingThread();
// imageProcessingThread.start();
//
// // When the screen is turned off and turned back on, the SurfaceTexture is already
// // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
// // a camera and start preview from here (otherwise, we wait until the surface is ready in
// // the SurfaceTextureListener).
// if (autoFitTextureView.isAvailable()) {
// openCamera(autoFitTextureView.getWidth(), autoFitTextureView.getHeight());
// } else {
// autoFitTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
// }
}
@Override
@ -511,10 +491,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
Size[] sizes = map.getOutputSizes(SurfaceTexture.class);
for (Size size : sizes) {
Log.i(TAG, size.toString());
}
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
@ -802,12 +778,6 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
public interface QrScanCallback {
void qrScanResult(String qrData);
}
public interface CameraReadyCallback {
void ready();
}
private CameraReadyCallback cameraReadyCallback;
private boolean readQrEnabled = true;
private BarcodeDetector barcodeDetector;
private RenderScript renderScript;
@ -865,6 +835,18 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
}
public void disablePreview() {
if (hideCamView.getAlpha() == 1 && hideCamView.getVisibility() == View.VISIBLE) {
closeCamera();
stopBackgroundThread();
if (imageProcessingThread.isAlive()) {
imageProcessingThread.isRunning = false;
}
return;
}
hideCamView.setAlpha(0f);
hideCamView.setVisibility(View.VISIBLE);
hideCamView.animate()
@ -896,19 +878,15 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
autoFitTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
hideCamView.setAlpha(1f);
hideCamView.setVisibility(View.VISIBLE);
hideCamView.setAlpha(1f);
hideCamView.animate()
.alpha(0f)
.setStartDelay(100)
.setDuration(1000)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
hideCamView.setVisibility(View.GONE);
if (cameraReadyCallback != null) {
cameraReadyCallback.ready();
}
}
});
}
@ -926,9 +904,5 @@ public class CameraFragment extends Fragment implements ActivityCompat.OnRequest
public void setOnQrAvailableListener(QrScanCallback callback) {
qrResultCallback = callback;
}
public void setCameraReadyCallback(CameraReadyCallback callback) {
this.cameraReadyCallback = callback;
}
}

View File

@ -0,0 +1,75 @@
package lu.circl.mispbump.fragments;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
public class SyncFragmentAdapter extends FragmentPagerAdapter {
public CameraFragment cameraFragment_1, cameraFragment_2;
private UploadSettingsFragment uploadSettingsFragment;
public SyncFragmentAdapter(@NonNull FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
private CameraFragment.QrScanCallback scanCallback;
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
if (cameraFragment_1 == null) {
cameraFragment_1 = new CameraFragment();
}
if (scanCallback != null) {
cameraFragment_1.setOnQrAvailableListener(scanCallback);
}
return cameraFragment_1;
case 1:
if (cameraFragment_2 == null) {
cameraFragment_2 = new CameraFragment();
}
if (scanCallback != null) {
cameraFragment_1.setOnQrAvailableListener(scanCallback);
}
return cameraFragment_2;
case 2:
if (uploadSettingsFragment == null) {
uploadSettingsFragment = new UploadSettingsFragment();
}
return uploadSettingsFragment;
default:
return new CameraFragment();
}
}
public void setQrReceivedCallback(CameraFragment.QrScanCallback qrScanCallback) {
this.scanCallback = qrScanCallback;
}
public void disableCameraPreview() {
if (cameraFragment_1 != null) {
cameraFragment_1.disablePreview();
}
if (cameraFragment_2 != null) {
cameraFragment_2.disablePreview();
}
}
@Override
public int getCount() {
return 3;
}
}

View File

@ -0,0 +1,24 @@
package lu.circl.mispbump.fragments;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import lu.circl.mispbump.R;
public class UploadInfoFragment extends Fragment {
public UploadInfoFragment () {}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_upload_info, container, false);
return v;
}
}

View File

@ -1,36 +1,63 @@
package lu.circl.mispbump.fragments;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Switch;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import lu.circl.mispbump.R;
import lu.circl.mispbump.customViews.MaterialPreferenceSwitch;
import lu.circl.mispbump.models.UploadInformation;
public class SyncOptionsFragment extends Fragment {
public class UploadSettingsFragment extends Fragment {
private Switch allowSelfSigned, push, pull, cache;
private MaterialPreferenceSwitch allowSelfSigned, push, pull, cache;
private UploadInformation uploadInformation;
public UploadSettingsFragment() {
}
public UploadSettingsFragment(UploadInformation uploadInformation) {
this.uploadInformation = uploadInformation;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_sync_options, container, false);
View v = inflater.inflate(R.layout.fragment_upload_settings, container, false);
allowSelfSigned = v.findViewById(R.id.self_signed_switch);
push = v.findViewById(R.id.push_switch);
pull = v.findViewById(R.id.pull_switch);
cache = v.findViewById(R.id.cache_switch);
populateContent();
return v;
}
private void populateContent() {
if (uploadInformation == null) {
return;
}
allowSelfSigned.setChecked(uploadInformation.isAllowSelfSigned());
push.setChecked(uploadInformation.isPush());
pull.setChecked(uploadInformation.isPull());
cache.setChecked(uploadInformation.isCached());
}
public void setUploadInfo(UploadInformation uploadInfo) {
this.uploadInformation = uploadInfo;
}
public boolean getAllowSelfSigned() {
return allowSelfSigned.isChecked();
}
public void setAllowSelfSigned(boolean allowSelfSigned) {
this.allowSelfSigned.setChecked(allowSelfSigned);
}
@ -38,6 +65,7 @@ public class SyncOptionsFragment extends Fragment {
public boolean getPush() {
return push.isChecked();
}
public void setPush(boolean push) {
this.push.setChecked(push);
}
@ -45,6 +73,7 @@ public class SyncOptionsFragment extends Fragment {
public boolean getPull() {
return pull.isChecked();
}
public void setPull(boolean pull) {
this.pull.setChecked(pull);
}
@ -52,6 +81,7 @@ public class SyncOptionsFragment extends Fragment {
public boolean getCache() {
return cache.isChecked();
}
public void setCache(boolean cache) {
this.cache.setChecked(cache);
}

View File

@ -2,14 +2,13 @@ package lu.circl.mispbump.models;
import androidx.annotation.NonNull;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
public class UploadInformation implements Serializable {
public class UploadInformation {
public enum SyncStatus {
COMPLETE,
@ -17,7 +16,7 @@ public class UploadInformation implements Serializable {
PENDING
}
private UUID id;
private UUID uuid;
private SyncStatus currentSyncStatus = SyncStatus.PENDING;
@ -29,19 +28,8 @@ public class UploadInformation implements Serializable {
private Date date;
public UploadInformation() {
this(null, null);
}
public UploadInformation(SyncInformation local) {
this(local, null);
}
public UploadInformation(SyncInformation local, SyncInformation remote) {
id = UUID.randomUUID();
uuid = UUID.randomUUID();
date = Calendar.getInstance().getTime();
this.local = local;
this.remote = remote;
}
// getter and setter
@ -67,11 +55,11 @@ public class UploadInformation implements Serializable {
return remote;
}
public UUID getId() {
return id;
public UUID getUuid() {
return uuid;
}
public void setId(UUID id) {
this.id = id;
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public void setDate() {

View File

@ -1,27 +0,0 @@
package lu.circl.mispbump.viewholders;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import lu.circl.mispbump.R;
public class UploadInfoListViewHolder extends RecyclerView.ViewHolder {
public View rootView;
public ImageView syncStatus;
public TextView orgName, date;
public UploadInfoListViewHolder(@NonNull View itemView) {
super(itemView);
rootView = itemView;
orgName = itemView.findViewById(R.id.orgName);
date = itemView.findViewById(R.id.date);
syncStatus = itemView.findViewById(R.id.syncStatus);
}
}

View File

@ -1,5 +1,9 @@
<vector android:height="24dp" android:tint="#B4B4B4"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,6v1.79c0,0.45 0.54,0.67 0.85,0.35l2.79,-2.79c0.2,-0.2 0.2,-0.51 0,-0.71l-2.79,-2.79c-0.31,-0.31 -0.85,-0.09 -0.85,0.36L12,4c-4.42,0 -8,3.58 -8,8 0,1.04 0.2,2.04 0.57,2.95 0.27,0.67 1.13,0.85 1.64,0.34 0.27,-0.27 0.38,-0.68 0.23,-1.04C6.15,13.56 6,12.79 6,12c0,-3.31 2.69,-6 6,-6zM17.79,8.71c-0.27,0.27 -0.38,0.69 -0.23,1.04 0.28,0.7 0.44,1.46 0.44,2.25 0,3.31 -2.69,6 -6,6v-1.79c0,-0.45 -0.54,-0.67 -0.85,-0.35l-2.79,2.79c-0.2,0.2 -0.2,0.51 0,0.71l2.79,2.79c0.31,0.31 0.85,0.09 0.85,-0.35L12,20c4.42,0 8,-3.58 8,-8 0,-1.04 -0.2,-2.04 -0.57,-2.95 -0.27,-0.67 -1.13,-0.85 -1.64,-0.34z"/>
</vector>

View File

@ -1,5 +1,9 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFF" android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:pathData="M12,7c0.55,0 1,0.45 1,1v4c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1L11,8c0,-0.55 0.45,-1 1,-1zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM13,17h-2v-2h2v2z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM11.78,7h-0.06c-0.4,0 -0.72,0.32 -0.72,0.72v4.72c0,0.35 0.18,0.68 0.49,0.86l4.15,2.49c0.34,0.2 0.78,0.1 0.98,-0.24 0.21,-0.34 0.1,-0.79 -0.25,-0.99l-3.87,-2.3L12.5,7.72c0,-0.4 -0.32,-0.72 -0.72,-0.72z"/>
</vector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white"/>
<corners android:radius="12dp"/>
</shape>

View File

@ -4,6 +4,6 @@
android:color="@color/white"/>
<corners
android:topLeftRadius="10dp"
android:topRightRadius="10dp"/>
android:topLeftRadius="12dp"
android:topRightRadius="12dp"/>
</shape>

View File

@ -4,8 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
@ -15,25 +14,23 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:background="@color/colorPrimary" />
android:theme="@style/ToolbarTheme"
app:popupTheme="@style/PopupTheme" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"/>
android:layout_height="match_parent" />
<TextView
android:id="@+id/empty"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawableTop="@drawable/ic_sync_black_24dp"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="64dp"
android:text="@string/no_syncs_hint"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton

View File

@ -39,7 +39,7 @@
android:tint="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<TextView
android:id="@+id/orgName"

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
@ -8,90 +7,153 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/sync_fragment_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
</FrameLayout>
<ImageView
android:id="@+id/qrcode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="64dp"
android:contentDescription="@string/qr_code"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
app:layout_behavior=".customViews.ExtendedBottomSheetBehavior"
android:id="@+id/bottomSheet"
android:layout_width="match_parent"
<FrameLayout
android:id="@+id/qrFrame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/rounded_rect"
android:backgroundTint="@color/colorPrimary"
app:behavior_peekHeight="64dp">
android:layout_gravity="center"
android:background="@drawable/rect_rounded"
android:backgroundTint="#99FFFFFF"
android:padding="8dp"
tools:layout_height="256dp"
tools:layout_width="256dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/prevButton"
android:layout_width="64dp"
android:layout_height="64dp"
android:background="?attr/selectableItemBackground"
android:padding="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow_back" />
<ImageView
android:visibility="invisible"
android:id="@+id/bottomSheetIcon"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_check_outline" />
<ImageButton
android:id="@+id/nextButton"
android:layout_width="64dp"
android:layout_height="64dp"
android:background="?attr/selectableItemBackground"
android:padding="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow_forward" />
</LinearLayout>
<TextView
android:id="@+id/bottomSheetText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="16dp"
android:gravity="top"
android:layout_gravity="center"
android:text="Received public key from partner"
android:textColor="@color/white" />
android:orientation="vertical"
android:gravity="center_vertical">
<ImageView
android:id="@+id/qrCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/qrHint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_info_outline"
android:drawablePadding="8dp"
android:drawableTint="@color/status_amber"
android:gravity="center_vertical"
android:padding="8dp"
android:text="Scan your partners QR code"
android:textColor="@color/colorPrimaryDark"/>
<TextView
android:id="@+id/qrHint2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_info_outline"
android:drawablePadding="8dp"
android:drawableTint="@color/colorPrimaryDark"
android:gravity="center_vertical"
android:padding="8dp"
android:lines="2"
android:text="Continue if your partner also scanned this QR code"
android:textColor="@color/colorPrimaryDark"/>
</LinearLayout>
</FrameLayout>
<LinearLayout
android:id="@+id/bottom"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
android:background="@drawable/rect_rounded_top"
android:backgroundTint="@color/colorPrimary"
android:orientation="horizontal">
<ImageButton
android:id="@+id/prevButton"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="?attr/selectableItemBackground"
android:padding="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow_back"/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
<ImageButton
android:id="@+id/nextButton"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="?attr/selectableItemBackground"
android:padding="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow_forward"/>
</LinearLayout>
<!--<LinearLayout-->
<!--app:layout_behavior=".customViews.ExtendedBottomSheetBehavior"-->
<!--android:id="@+id/bottomSheet"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content"-->
<!--android:orientation="vertical"-->
<!--android:background="@drawable/rect_rounded_top"-->
<!--android:backgroundTint="@color/colorPrimary"-->
<!--app:behavior_peekHeight="64dp">-->
<!--<LinearLayout-->
<!--android:orientation="horizontal"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content">-->
<!--<ImageButton-->
<!--android:id="@+id/prevButton"-->
<!--android:layout_width="64dp"-->
<!--android:layout_height="64dp"-->
<!--android:background="?attr/selectableItemBackground"-->
<!--android:padding="8dp"-->
<!--app:layout_constraintStart_toStartOf="parent"-->
<!--app:layout_constraintTop_toTopOf="parent"-->
<!--app:srcCompat="@drawable/ic_arrow_back" />-->
<!--<ImageView-->
<!--android:visibility="invisible"-->
<!--android:id="@+id/bottomSheetIcon"-->
<!--android:layout_weight="1"-->
<!--android:layout_width="0dp"-->
<!--android:layout_height="32dp"-->
<!--android:layout_gravity="center_vertical"-->
<!--android:src="@drawable/ic_check_outline" />-->
<!--<ImageButton-->
<!--android:id="@+id/nextButton"-->
<!--android:layout_width="64dp"-->
<!--android:layout_height="64dp"-->
<!--android:background="?attr/selectableItemBackground"-->
<!--android:padding="8dp"-->
<!--app:layout_constraintEnd_toEndOf="parent"-->
<!--app:layout_constraintTop_toTopOf="parent"-->
<!--app:srcCompat="@drawable/ic_arrow_forward" />-->
<!--</LinearLayout>-->
<!--<TextView-->
<!--android:id="@+id/bottomSheetText"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:paddingStart="16dp"-->
<!--android:paddingEnd="16dp"-->
<!--android:paddingBottom="16dp"-->
<!--android:gravity="top"-->
<!--android:layout_gravity="center"-->
<!--android:text="Received public key from partner"-->
<!--android:textColor="@color/white" />-->
<!--</LinearLayout>-->
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -34,10 +34,10 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="Check if instance is available" />
app:layout_constraintEnd_toEndOf="parent"
app:description="Check if instance is available" />
<lu.circl.mispbump.customViews.UploadAction
android:id="@+id/orgAction"
@ -46,10 +46,10 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="Add organisation" />
app:layout_constraintEnd_toEndOf="parent"
app:description="Add organisation" />
<lu.circl.mispbump.customViews.UploadAction
android:id="@+id/userAction"
@ -61,7 +61,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="Add sync user" />
app:description="Add sync user" />
<lu.circl.mispbump.customViews.UploadAction
android:id="@+id/serverAction"
@ -73,18 +73,18 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="Add sync server" />
app:description="Add sync server" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:backgroundTint="@color/colorAccent"
android:tint="@color/white"
android:src="@drawable/ic_cloud_upload"/>
<!--<com.google.android.material.floatingactionbutton.FloatingActionButton-->
<!--android:id="@+id/fab"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_gravity="bottom|end"-->
<!--android:layout_margin="16dp"-->
<!--app:backgroundTint="@color/colorAccent"-->
<!--android:tint="@color/white"-->
<!--android:src="@drawable/ic_cloud_upload"/>-->
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootLayout"
android:transitionName="root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="6dp"
app:elevation="6dp">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat"
android:background="@color/white" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/syncStatus"
android:transitionName="icon"
android:transformPivotX="32dp"
android:transformPivotY="32dp"
android:layout_width="64dp"
android:layout_height="64dp"
android:tint="@color/status_green"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:src="@drawable/ic_check_outline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/orgName"
android:transitionName="title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:textAppearance="@style/Text.Title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/syncStatus"
tools:text="Organisation Title" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/ic_cloud_upload"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/PopupTheme"
app:theme="@style/ToolbarTheme.Light"/>
<TextView
android:id="@+id/syncStatus"
android:layout_marginTop="-12dp"
android:layout_marginBottom="8dp"
tools:text="Successfully uploaded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle"
android:textColor="@color/status_green"
android:gravity="center_vertical"
android:layout_marginStart="72dp"
android:drawableEnd="@drawable/ic_check_outline"
android:drawableTint="@color/status_green"
android:drawablePadding="8dp"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="fixed"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="8dp"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
tools:drawableEnd="@drawable/ic_error_outline"
tools:drawableTint="@color/status_red"
tools:text="Sample title"/>
<ImageView
android:src="@drawable/ic_error_outline"
android:tint="@color/status_red"
android:layout_width="24dp"
android:layout_height="24dp"/>
</LinearLayout>

View File

@ -1,147 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/allow_self_signed"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:fontFamily="sans-serif"
android:text="Allow self signed certificates"
android:textColor="@android:color/primary_text_light"
android:textSize="14sp"
app:layout_constraintEnd_toStartOf="@+id/self_signed_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/share_events_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:text="Server certificate must not be from a trusted CA (not recomended)"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@+id/self_signed_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/allow_self_signed" />
<Switch
android:id="@+id/self_signed_switch"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="32dp"
app:layout_constraintBottom_toBottomOf="@+id/share_events_desc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/allow_self_signed" />
<TextView
android:id="@+id/push_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:fontFamily="sans-serif"
android:text="Push"
android:textColor="#000000"
android:textSize="14sp"
app:layout_constraintEnd_toStartOf="@+id/push_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/share_events_desc" />
<TextView
android:id="@+id/push_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:text="Allow the upload of events and their attributes"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@+id/push_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/push_title" />
<Switch
android:id="@+id/push_switch"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="32dp"
app:layout_constraintBottom_toBottomOf="@+id/push_desc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/push_title" />
<TextView
android:id="@+id/pull_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:text="Pull"
android:textColor="#000000"
app:layout_constraintEnd_toStartOf="@+id/pull_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/push_desc" />
<TextView
android:id="@+id/pull_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:text="Allow the download of events and their attributes"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@+id/pull_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pull_title" />
<Switch
android:id="@+id/pull_switch"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="32dp"
app:layout_constraintBottom_toBottomOf="@+id/pull_desc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/pull_title" />
<Switch
android:id="@+id/cache_switch"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="32dp"
app:layout_constraintBottom_toBottomOf="@+id/cache_desc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/cache_title" />
<TextView
android:id="@+id/cache_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:text="Caching"
android:textColor="#000000"
app:layout_constraintEnd_toStartOf="@+id/cache_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pull_desc" />
<TextView
android:id="@+id/cache_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:text="Allow the caching of events and their attributes"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@+id/cache_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cache_title" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="This Organisation will have the following rights on your MISP instance"
android:textAppearance="@style/Text.Title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<lu.circl.mispbump.customViews.MaterialPreferenceSwitch
android:id="@+id/self_signed_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text"
app:subtitle="Server certificate must not be from a trusted CA (not recomended)"
app:title="Allow self signed certificates"/>
<lu.circl.mispbump.customViews.MaterialPreferenceSwitch
android:id="@+id/push_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/self_signed_switch"
app:subtitle="Allow the upload of events and their attributes"
app:title="Push"/>
<lu.circl.mispbump.customViews.MaterialPreferenceSwitch
android:id="@+id/pull_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/push_switch"
app:subtitle="Allow the download of events and their attributes"
app:title="Pull"/>
<lu.circl.mispbump.customViews.MaterialPreferenceSwitch
android:id="@+id/cache_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/pull_switch"
app:subtitle="Allow the caching of events and their attributes"
app:title="Cache"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootLayout"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?attr/selectableItemBackground">
<TextView
android:id="@+id/material_preference_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:textAppearance="@style/Text.Title"
app:layout_constraintEnd_toStartOf="@id/material_preference_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<TextView
android:id="@+id/material_preference_subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:textAppearance="@style/Text.SubTitle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/material_preference_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/material_preference_title"
tools:text="Subtitle" />
<Switch
android:id="@+id/material_preference_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="@id/material_preference_subtitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/material_preference_title" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -13,11 +13,13 @@
<TextView
android:id="@+id/title"
android:textAppearance="@style/Text.Title"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="000"
android:layout_width="0dp"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="@style/Text.Title"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp" />
<ImageView
android:id="@+id/stateView"
@ -36,6 +38,7 @@
<TextView
android:id="@+id/error"
android:text="000"
android:textAppearance="@style/Text.SubTitle"
android:textColor="@color/status_red"
android:layout_marginEnd="24dp"

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:foreground="?attr/selectableItemBackground">
<ImageView
android:id="@+id/stateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_error_outline"
android:tint="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:visibility="gone"
android:id="@+id/progressBar"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title (upload action)" />
<TextView
android:visibility="gone"
android:id="@+id/error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Error" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2,8 +2,8 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/bla"
android:id="@+id/delete"
android:icon="@drawable/ic_delete_forever"
android:title="delete"
app:showAsAction="ifRoom"/>
android:title="Remove"
app:showAsAction="never"/>
</menu>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds/>
<!--<fade-->
<!--android:fadingMode="fade_in_out"/>-->
<!--<transition class="lu.circl.mispbump.RecyclerViewItemTransition"/>-->
</transitionSet>

View File

@ -1,9 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="title" format="string"/>
<attr name="subtitle" format="string"/>
<declare-styleable name="MaterialPreferenceText">
<attr name="pref_icon" format="integer"/>
<attr name="title" format="string"/>
<attr name="subtitle" format="string"/>
<attr name="title"/>
<attr name="subtitle"/>
</declare-styleable>
<declare-styleable name="MaterialPreferenceSwitch">
<attr name="title"/>
<attr name="subtitle"/>
</declare-styleable>
<declare-styleable name="UploadAction">

View File

@ -2,17 +2,17 @@
<resources>
<color name="colorPrimary">#047EB4</color>
<color name="colorPrimaryDark">#023850</color>
<!--<color name="colorPrimaryDark">#047EB4</color>-->
<color name="colorAccent">#12B3FA</color>
<color name="colorAccent_50">#8012B3FA</color>
<color name="dividerColor">#33000000</color>
<!-- colors -->
<color name="white">#FFFFFF</color>
<color name="white_50">#80FFFFFF</color>
<color name="grey">#BDBDBD</color>
<color name="grey_light">#F0F0F0</color>
<color name="black">#000</color>
<color name="status_green">#4CAF50</color>
<color name="status_amber">#FB8C00</color>

View File

@ -6,7 +6,6 @@
<string name="misp_automation_hint">MISP Automation Key</string>
<string name="no_information">No Information</string>
<string name="save_automation_key_hint">Save Automation Key</string>
<string name="home" translatable="false">Home</string>
<string name="menu_login_help_label">Help</string>
<string name="login_help_text"><b>MISP Server URL</b>\nPublic MISP URL\n\n<b>MISP Automation key</b>\nZu finden unter ...</string>
<string name="ok" translatable="false">Okay</string>

View File

@ -1,21 +1,8 @@
<resources>
<!-- Theme.MaterialComponents.Light.NoActionBar || Theme.AppCompat.Light.NoActionBar -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowContentTransitions">true</item>
<!-- specify enter and exit transitions -->
<!-- options are: explode, slide, fade -->
<!--<item name="android:windowEnterTransition">@transition/simple</item>-->
<!--<item name="android:windowExitTransition">@transition/simple</item>-->
<!--specify shared element transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/simple</item>
<item name="android:windowSharedElementExitTransition">@transition/simple</item>
</style>
<style name="AppTheme.Translucent">
@ -23,13 +10,14 @@
<item name="android:windowTranslucentStatus">true</item>
</style>
<style name="AppTheme.FullScreenDialog" parent="Theme.MaterialComponents.Light.DialogWhenLarge">
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
</style>
<style name="PopupTheme" parent="ThemeOverlay.MaterialComponents.Light"/>
<style name="DialogAnimation">
<item name="android:windowEnterAnimation">@anim/fade_in</item>
<item name="android:windowExitAnimation">@anim/fade_out</item>
<style name="ToolbarTheme" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar"/>
<style name="ToolbarTheme.Light" parent="@style/ThemeOverlay.MaterialComponents.ActionBar">
<item name="android:background">@color/white</item>
<item name="colorControlNormal">#000</item>
<item name="android:tint">#000</item>
</style>
<style name="Text"/>