upload is now safe in every case

pull/5/head
Felix Prahl-Kamps 2019-07-03 23:13:25 +02:00
parent 9268a0939c
commit 4585dedc7a
11 changed files with 318 additions and 190 deletions

View File

@ -36,6 +36,7 @@
android:theme="@style/AppTheme.Translucent" /> android:theme="@style/AppTheme.Translucent" />
<activity <activity
android:name=".activities.UploadInfoActivity" android:name=".activities.UploadInfoActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName=".activities.HomeActivity" /> android:parentActivityName=".activities.HomeActivity" />
<activity <activity
android:name=".activities.UploadActivity" android:name=".activities.UploadActivity"

View File

@ -12,16 +12,13 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import java.io.IOException;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import lu.circl.mispbump.R; import lu.circl.mispbump.R;
import lu.circl.mispbump.auxiliary.MispRestClient;
import lu.circl.mispbump.auxiliary.PreferenceManager; import lu.circl.mispbump.auxiliary.PreferenceManager;
import lu.circl.mispbump.customViews.UploadAction; import lu.circl.mispbump.customViews.UploadAction;
import lu.circl.mispbump.models.UploadInformation; import lu.circl.mispbump.models.UploadInformation;
import lu.circl.mispbump.auxiliary.MispRestClient;
import lu.circl.mispbump.models.restModels.MispServer;
import lu.circl.mispbump.models.restModels.Organisation; import lu.circl.mispbump.models.restModels.Organisation;
import lu.circl.mispbump.models.restModels.Server; import lu.circl.mispbump.models.restModels.Server;
import lu.circl.mispbump.models.restModels.User; import lu.circl.mispbump.models.restModels.User;
@ -37,6 +34,63 @@ public class UploadActivity extends AppCompatActivity {
private MispRestClient restClient; private MispRestClient restClient;
private UploadAction availableAction, orgAction, userAction, serverAction; private UploadAction availableAction, orgAction, userAction, serverAction;
private MispRestClient.AvailableCallback availableCallback = new MispRestClient.AvailableCallback() {
@Override
public void available() {
mispAvailable(true, null);
}
@Override
public void unavailable(String error) {
mispAvailable(false, error);
}
};
private MispRestClient.OrganisationCallback organisationCallback = new MispRestClient.OrganisationCallback() {
@Override
public void success(Organisation organisation) {
organisationAdded(organisation);
}
@Override
public void failure(String error) {
organisationAdded(null);
}
};
private MispRestClient.UserCallback userCallback = new MispRestClient.UserCallback() {
@Override
public void success(User user) {
userAdded(user);
}
@Override
public void failure(String error) {
userAdded(null);
}
};
private MispRestClient.AllServersCallback allServersCallback = new MispRestClient.AllServersCallback() {
@Override
public void success(Server[] servers) {
allServersReceived(servers);
}
@Override
public void failure(String error) {
allServersReceived(null);
}
};
private MispRestClient.ServerCallback serverCallback = new MispRestClient.ServerCallback() {
@Override
public void success(Server server) {
serverAdded(server);
}
@Override
public void failure(String error) {
serverAdded(null);
}
};
private boolean errorWhileUpload; private boolean errorWhileUpload;
@Override @Override
@ -121,17 +175,12 @@ public class UploadActivity extends AppCompatActivity {
restClient.isAvailable(availableCallback); restClient.isAvailable(availableCallback);
} }
private User generateSyncUser(Organisation organisation) { private User generateSyncUser(Organisation organisation) {
User syncUser = new User(); User syncUser = new User();
syncUser.org_id = organisation.id; syncUser.org_id = organisation.id;
syncUser.role_id = User.ROLE_SYNC_USER; syncUser.role_id = User.ROLE_SYNC_USER;
syncUser.email = uploadInformation.getRemote().syncUserEmail; syncUser.email = uploadInformation.getRemote().syncUserEmail;
// String emailSaveOrgName = organisation.name.replace(" ", "").toLowerCase();
// String syncUserEmailFormat = uploadInformation.getRemote().syncUserEmail;
// syncUser.email = syncUserEmailFormat.replace("[ORG]", emailSaveOrgName);
// uploadInformation.getLocal().syncUserEmail = syncUser.email;
syncUser.password = uploadInformation.getRemote().syncUserPassword; syncUser.password = uploadInformation.getRemote().syncUserPassword;
syncUser.authkey = uploadInformation.getRemote().syncUserAuthkey; syncUser.authkey = uploadInformation.getRemote().syncUserAuthkey;
syncUser.termsaccepted = true; syncUser.termsaccepted = true;
@ -139,24 +188,35 @@ public class UploadActivity extends AppCompatActivity {
return syncUser; return syncUser;
} }
private Server generateSyncServer() {
Server server = new Server();
server.name = uploadInformation.getRemote().organisation.name + "'s Sync Server";
server.url = uploadInformation.getRemote().baseUrl;
server.remote_org_id = uploadInformation.getRemote().organisation.id;
server.authkey = uploadInformation.getLocal().syncUserAuthkey;
server.pull = uploadInformation.isPull();
server.push = uploadInformation.isPush();
server.caching_enabled = uploadInformation.isCached();
server.self_signed = uploadInformation.isAllowSelfSigned();
return server;
}
private MispRestClient.AvailableCallback availableCallback = new MispRestClient.AvailableCallback() {
@Override private void mispAvailable(boolean available, String error) {
public void available() { if (available) {
availableAction.setCurrentUploadState(UploadAction.UploadState.DONE); availableAction.setCurrentUploadState(UploadAction.UploadState.DONE);
orgAction.setCurrentUploadState(UploadAction.UploadState.LOADING); availableAction.setError(null);
restClient.addOrganisation(uploadInformation.getRemote().organisation, organisationCallback);
}
@Override restClient.addOrganisation(uploadInformation.getRemote().organisation, organisationCallback);
public void unavailable(String error) { } else {
availableAction.setCurrentUploadState(UploadAction.UploadState.ERROR); availableAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
availableAction.setError(error); availableAction.setError(error);
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
errorWhileUpload = true; errorWhileUpload = true;
Snackbar sb = Snackbar.make(rootLayout, error, Snackbar.LENGTH_INDEFINITE); Snackbar sb = Snackbar.make(rootLayout, error, Snackbar.LENGTH_INDEFINITE);
sb.setAction("Retry", new View.OnClickListener() { sb.setAction("Retry", new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -166,103 +226,83 @@ public class UploadActivity extends AppCompatActivity {
startUpload(); startUpload();
} }
}); });
sb.show(); sb.show();
} }
}; }
private MispRestClient.OrganisationCallback organisationCallback = new MispRestClient.OrganisationCallback() { private void organisationAdded(Organisation organisation) {
@Override if (organisation != null) {
public void success(Organisation organisation) {
orgAction.setCurrentUploadState(UploadAction.UploadState.DONE); orgAction.setCurrentUploadState(UploadAction.UploadState.DONE);
userAction.setCurrentUploadState(UploadAction.UploadState.LOADING);
// for later reference in add user callback
uploadInformation.getRemote().organisation.id = organisation.id; uploadInformation.getRemote().organisation.id = organisation.id;
restClient.addUser(generateSyncUser(organisation), userCallback); restClient.addUser(generateSyncUser(organisation), userCallback);
} } else {
restClient.getOrganisation(uploadInformation.getRemote().organisation.uuid, new MispRestClient.OrganisationCallback() {
@Override @Override
public void failure(String error) { public void success(Organisation organisation) {
orgAction.setCurrentUploadState(UploadAction.UploadState.ERROR); organisationAdded(organisation);
orgAction.setError(error);
errorWhileUpload = true;
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
}
};
private MispRestClient.UserCallback userCallback = new MispRestClient.UserCallback() {
@Override
public void success(User user) {
userAction.setCurrentUploadState(UploadAction.UploadState.DONE);
Server server = new Server();
server.name = uploadInformation.getRemote().organisation.name + "'s Sync Server";
server.url = uploadInformation.getRemote().baseUrl;
server.remote_org_id = uploadInformation.getRemote().organisation.id;
server.authkey = uploadInformation.getLocal().syncUserAuthkey;
server.pull = uploadInformation.isPull();
server.push = uploadInformation.isPush();
server.caching_enabled = uploadInformation.isCached();
server.self_signed = uploadInformation.isAllowSelfSigned();
restClient.addServer(server, serverCallback);
}
@Override
public void failure(String error) {
userAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
userAction.setError(error);
errorWhileUpload = true;
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
}
};
private MispRestClient.ServerCallback serverCallback = new MispRestClient.ServerCallback() {
@Override
public void success(List<MispServer> servers) {
}
@Override
public void success(Server server) {
serverAction.setCurrentUploadState(UploadAction.UploadState.DONE);
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.COMPLETE);
saveCurrentState();
}
@Override
public void failure(String error) {
serverAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
serverAction.setError(error);
errorWhileUpload = true;
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
}
};
private int organisationExists() throws IOException {
final UUID uuidToCheck = UUID.fromString(uploadInformation.getRemote().organisation.uuid);
Organisation[] organisations = restClient.getAllOrganisations();
if (organisations != null) {
for (Organisation organisation : organisations) {
if (uuidToCheck.compareTo(UUID.fromString(organisation.uuid)) == 0) {
return organisation.id;
} }
@Override
public void failure(String error) {
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
orgAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
orgAction.setError(error);
errorWhileUpload = true;
}
});
}
}
private void userAdded(User user) {
if (user != null) {
userAction.setCurrentUploadState(UploadAction.UploadState.DONE);
restClient.getAllServers(allServersCallback);
} else {
restClient.getUser(uploadInformation.getRemote().syncUserEmail, new MispRestClient.UserCallback() {
@Override
public void success(User user) {
userAdded(user);
}
@Override
public void failure(String error) {
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
userAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
userAction.setError(error);
errorWhileUpload = true;
}
});
}
}
private void allServersReceived(Server[] servers) {
Server serverToUpload = generateSyncServer();
for (Server server : servers) {
if (server.remote_org_id.equals(serverToUpload.remote_org_id)) {
// server already exists
serverToUpload.id = server.id;
break;
} }
} }
return -1; restClient.addServer(serverToUpload, serverCallback);
} }
private int userExists() { private void serverAdded(Server server) {
if (server != null) {
return -1; serverAction.setCurrentUploadState(UploadAction.UploadState.DONE);
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.COMPLETE);
saveCurrentState();
} else {
uploadInformation.setCurrentSyncStatus(UploadInformation.SyncStatus.FAILURE);
serverAction.setCurrentUploadState(UploadAction.UploadState.ERROR);
serverAction.setError("Could not add server");
errorWhileUpload = true;
}
} }
} }

View File

@ -5,6 +5,7 @@ import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -46,8 +47,8 @@ public class UploadInfoActivity extends AppCompatActivity {
preferenceManager = PreferenceManager.getInstance(UploadInfoActivity.this); preferenceManager = PreferenceManager.getInstance(UploadInfoActivity.this);
// tint statusBar // tint statusBar
getWindow().setStatusBarColor(getColor(R.color.white)); getWindow().setStatusBarColor(getColor(R.color.colorPrimary));
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); // getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
parseExtra(); parseExtra();
initToolbar(); initToolbar();
@ -114,9 +115,12 @@ public class UploadInfoActivity extends AppCompatActivity {
ActionBar ab = getSupportActionBar(); ActionBar ab = getSupportActionBar();
assert ab != null; assert ab != null;
ab.setTitle(uploadInformation.getRemote().organisation.name); TextView toolbarTitle = findViewById(R.id.toolbarTitle);
toolbarTitle.setText(uploadInformation.getRemote().organisation.name);
ab.setHomeAsUpIndicator(R.drawable.ic_close); ab.setHomeAsUpIndicator(R.drawable.ic_close);
ab.setDisplayShowTitleEnabled(true);
ab.setDisplayShowTitleEnabled(false);
ab.setDisplayHomeAsUpEnabled(true); ab.setDisplayHomeAsUpEnabled(true);
} }

View File

@ -57,17 +57,28 @@ public class MispRestClient {
void failure(String error); void failure(String error);
} }
public interface AllUsersCallback {
void success(User[] users);
void failure(String error);
}
public interface OrganisationCallback { public interface OrganisationCallback {
void success(Organisation organisation); void success(Organisation organisation);
void failure(String error);
}
public interface AllOrganisationsCallback {
void success(Organisation[] organisations);
void failure(String error); void failure(String error);
} }
public interface ServerCallback { public interface ServerCallback {
void success(List<MispServer> servers);
void success(Server server); void success(Server server);
void failure(String error);
}
public interface AllServersCallback {
void success(Server[] servers);
void failure(String error); void failure(String error);
} }
@ -100,7 +111,7 @@ public class MispRestClient {
Retrofit retrofit = new Retrofit.Builder() Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url) .baseUrl(url)
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())
.client(getCustomClient(true, false)) .client(getCustomClient(true, true))
.build(); .build();
mispRestInterface = retrofit.create(MispRestInterface.class); mispRestInterface = retrofit.create(MispRestInterface.class);
@ -250,6 +261,7 @@ public class MispRestClient {
* @param userId user identifier * @param userId user identifier
* @param callback {@link UserCallback} wrapper to return user directly * @param callback {@link UserCallback} wrapper to return user directly
*/ */
public void getUser(int userId, final UserCallback callback) { public void getUser(int userId, final UserCallback callback) {
Call<MispUser> call = mispRestInterface.getUser(userId); Call<MispUser> call = mispRestInterface.getUser(userId);
@ -275,6 +287,57 @@ public class MispRestClient {
}); });
} }
public void getUser(final String emailAddress, final UserCallback callback) {
getAllUsers(new AllUsersCallback() {
@Override
public void success(User[] users) {
for (User user : users) {
if (user.email.equals(emailAddress)) {
callback.success(user);
return;
}
}
callback.failure("Could not find user with email address {" + emailAddress + "}");
}
@Override
public void failure(String error) {
callback.failure(error);
}
});
}
public void getAllUsers(final AllUsersCallback callback) {
Call<List<MispUser>> call = mispRestInterface.getAllUsers();
call.enqueue(new Callback<List<MispUser>>() {
@Override
public void onResponse(Call<List<MispUser>> call, Response<List<MispUser>> response) {
if (!response.isSuccessful()) {
callback.failure("Failed onResponse");
return;
}
List<MispUser> mispUsers = response.body();
assert mispUsers != null;
User[] users = new User[mispUsers.size()];
for (int i = 0; i < users.length; i++) {
users[i] = mispUsers.get(i).user;
}
callback.success(users);
}
@Override
public void onFailure(Call<List<MispUser>> call, Throwable t) {
callback.failure(extractError(t));
}
});
}
/** /**
* Add a given user to the MISP instance referenced by url in preferences. * Add a given user to the MISP instance referenced by url in preferences.
* *
@ -335,45 +398,56 @@ public class MispRestClient {
}); });
} }
public Organisation[] getAllOrganisations() throws IOException { public void getOrganisation(final String uuid, final OrganisationCallback callback) {
getAllOrganisations(new AllOrganisationsCallback() {
@Override
public void success(Organisation[] organisations) {
for (Organisation organisation : organisations) {
if (organisation.uuid.equals(uuid)) {
callback.success(organisation);
return;
}
}
callback.failure("Could not find organisation with UUID {" + uuid + "}");
}
@Override
public void failure(String error) {
callback.failure(error);
}
});
}
public void getAllOrganisations(final AllOrganisationsCallback callback) {
Call<List<MispOrganisation>> call = mispRestInterface.getAllOrganisations(); Call<List<MispOrganisation>> call = mispRestInterface.getAllOrganisations();
Response<List<MispOrganisation>> response = call.execute();
List<MispOrganisation> mispOrganisations = response.body(); call.enqueue(new Callback<List<MispOrganisation>>() {
Organisation[] organisations = new Organisation[mispOrganisations.size()]; @Override
public void onResponse(Call<List<MispOrganisation>> call, Response<List<MispOrganisation>> response) {
if (!response.isSuccessful()) {
// TODO handle
return;
}
for (int i = 0; i < mispOrganisations.size(); i++) { List<MispOrganisation> mispOrganisations = response.body();
organisations[i] = mispOrganisations.get(i).organisation;
}
return organisations; assert mispOrganisations != null;
// call.enqueue(new Callback<List<MispOrganisation>>() { Organisation[] organisations = new Organisation[mispOrganisations.size()];
// @Override
// public void onResponse(Call<List<MispOrganisation>> call, Response<List<MispOrganisation>> response) { for (int i = 0; i < mispOrganisations.size(); i++) {
// if (!response.isSuccessful()) { organisations[i] = mispOrganisations.get(i).organisation;
// // TODO handle }
// return;
// } callback.success(organisations);
// }
// List<MispOrganisation> mispOrganisations = response.body();
// @Override
// assert mispOrganisations != null; public void onFailure(Call<List<MispOrganisation>> call, Throwable t) {
// callback.failure(extractError(t));
// Organisation[] organisations = new Organisation[mispOrganisations.size()]; }
// });
// for (int i = 0; i < mispOrganisations.size(); i++) {
// organisations[i] = mispOrganisations.get(i).organisation;
// }
//
// callback.success(organisations);
// }
//
// @Override
// public void onFailure(Call<List<MispOrganisation>> call, Throwable t) {
// callback.failure(extractError(t));
// }
// });
} }
/** /**
@ -405,13 +479,17 @@ public class MispRestClient {
// --- server routes --- // --- server routes ---
public void getServer() {
}
/** /**
* Get all servers on MISP instance. * Get all servers on MISP instance.
* *
* @param callback {@link OrganisationCallback} wrapper to return a list of servers directly * @param callback {@link OrganisationCallback} wrapper to return a list of servers directly
*/ */
public void getServers(final ServerCallback callback) { public void getAllServers(final AllServersCallback callback) {
Call<List<MispServer>> call = mispRestInterface.getServers(); Call<List<MispServer>> call = mispRestInterface.getAllServers();
call.enqueue(new Callback<List<MispServer>>() { call.enqueue(new Callback<List<MispServer>>() {
@Override @Override
@ -419,7 +497,17 @@ public class MispRestClient {
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
callback.failure(extractError(response)); callback.failure(extractError(response));
} else { } else {
callback.success(response.body());
List<MispServer> mispServers = response.body();
assert mispServers != null;
Server[] servers = new Server[mispServers.size()];
for (int i = 0; i < servers.length; i++) {
servers[i] = mispServers.get(i).server;
}
callback.success(servers);
} }
} }
@ -452,6 +540,7 @@ public class MispRestClient {
@Override @Override
public void onFailure(Call<Server> call, Throwable t) { public void onFailure(Call<Server> call, Throwable t) {
callback.failure(t.getMessage()); callback.failure(t.getMessage());
throw new RuntimeException(t);
} }
}); });
} }

View File

@ -53,6 +53,8 @@ public class UploadAction extends ConstraintLayout {
errorView = baseView.findViewById(R.id.error); errorView = baseView.findViewById(R.id.error);
stateView = baseView.findViewById(R.id.stateView); stateView = baseView.findViewById(R.id.stateView);
progressBar = baseView.findViewById(R.id.progressBar); progressBar = baseView.findViewById(R.id.progressBar);
setCurrentUploadState(UploadState.PENDING);
} }
@ -85,7 +87,7 @@ public class UploadAction extends ConstraintLayout {
case DONE: case DONE:
stateView.setVisibility(VISIBLE); stateView.setVisibility(VISIBLE);
stateView.setImageResource(R.drawable.ic_check); stateView.setImageResource(R.drawable.ic_check_outline);
ImageViewCompat.setImageTintList(stateView, ColorStateList.valueOf(context.getColor(R.color.status_green))); ImageViewCompat.setImageTintList(stateView, ColorStateList.valueOf(context.getColor(R.color.status_green)));
break; break;

View File

@ -1,24 +0,0 @@
package lu.circl.mispbump.fragments;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import lu.circl.mispbump.R;
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

@ -33,6 +33,9 @@ public interface MispRestInterface {
@GET("users/view/{value}") @GET("users/view/{value}")
Call<MispUser> getUser(@Path("value") int userId); Call<MispUser> getUser(@Path("value") int userId);
@GET("admin/users")
Call<List<MispUser>> getAllUsers();
@POST("admin/users/add") @POST("admin/users/add")
Call<MispUser> addUser(@Body User user); Call<MispUser> addUser(@Body User user);
@ -50,7 +53,7 @@ public interface MispRestInterface {
// server routes // server routes
@GET("servers/index") @GET("servers/index")
Call<List<MispServer>> getServers(); Call<List<MispServer>> getAllServers();
@POST("servers/add") @POST("servers/add")
Call<Server> addServer(@Body Server server); Call<Server> addServer(@Body Server server);

View File

@ -34,6 +34,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@color/white"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -44,6 +45,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@color/white"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -54,6 +56,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@color/white"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@ -64,21 +67,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@color/white"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:description="Add sync server" /> app:description="Add sync server" />
</androidx.appcompat.widget.LinearLayoutCompat> </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"/>-->
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -2,6 +2,7 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootLayout" android:id="@+id/rootLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -9,19 +10,34 @@
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/white"> android:background="@color/colorPrimary">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/PopupTheme" app:popupTheme="@style/PopupTheme"
app:theme="@style/ToolbarTheme.Light"/> app:theme="@style/ToolbarTheme">
<TextView
android:id="@+id/toolbarTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
tools:text="Organisation A"
android:layout_gravity="center"/>
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout" android:id="@+id/tabLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:tabBackground="@color/colorPrimary"
app:tabSelectedTextColor="@color/white"
app:tabTextColor="@color/white_50"
app:tabIndicatorFullWidth="true"
app:tabGravity="fill" app:tabGravity="fill"
app:tabMode="fixed"/> app:tabMode="fixed"/>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@ -9,13 +9,15 @@
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content"
android:paddingBottom="32dp">
<lu.circl.mispbump.customViews.MaterialPreferenceText <lu.circl.mispbump.customViews.MaterialPreferenceText
android:id="@+id/baseUrl" android:id="@+id/baseUrl"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:background="@color/white"
app:subtitle="www.google.de" app:subtitle="www.google.de"
app:title="Base URL"/> app:title="Base URL"/>
@ -24,6 +26,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:background="@color/white"
app:title="Authkey" app:title="Authkey"
app:password="abc"/> app:password="abc"/>
@ -32,6 +35,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:background="@color/white"
android:elevation="4dp"
app:title="Password" app:title="Password"
app:password="abc"/> app:password="abc"/>
</LinearLayout> </LinearLayout>

View File

@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="16dp" android:padding="16dp"
android:foreground="?attr/selectableItemBackground"> android:foreground="?attr/selectableItemBackgroundBorderless">
<ImageView <ImageView
android:id="@+id/stateView" android:id="@+id/stateView"