Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ public void bind(@NonNull Account localAccount, @NonNull Consumer<Account> onAcc
.apply(RequestOptions.circleCropTransform())
.into(binding.accountItemAvatar);
itemView.setOnClickListener((v) -> onAccountClick.accept(localAccount));
binding.delete.setVisibility(View.GONE);
binding.accountContextMenu.setVisibility(View.GONE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@ public class ManageAccountAdapter extends RecyclerView.Adapter<ManageAccountView
private final Consumer<Account> onAccountClick;
@NonNull
private final Consumer<Account> onAccountDelete;
@NonNull
Consumer<Account> onChangeNotesPath;
@NonNull
Consumer<Account> onChangeFileSuffix;

public ManageAccountAdapter(@NonNull Consumer<Account> onAccountClick, @NonNull Consumer<Account> onAccountDelete) {
public ManageAccountAdapter(@NonNull Consumer<Account> onAccountClick,
@NonNull Consumer<Account> onAccountDelete,
@NonNull Consumer<Account> onChangeNotesPath,
@NonNull Consumer<Account> onChangeFileSuffix) {
this.onAccountClick = onAccountClick;
this.onAccountDelete = onAccountDelete;
this.onChangeNotesPath = onChangeNotesPath;
this.onChangeFileSuffix = onChangeFileSuffix;
setHasStableIds(true);
}

Expand All @@ -48,7 +57,7 @@ public void onBindViewHolder(@NonNull ManageAccountViewHolder holder, int positi
holder.bind(localAccount, (localAccountClicked) -> {
setCurrentLocalAccount(localAccountClicked);
onAccountClick.accept(localAccountClicked);
}, onAccountDelete, currentLocalAccount != null && currentLocalAccount.getId() == localAccount.getId());
}, onAccountDelete, onChangeNotesPath, onChangeFileSuffix, currentLocalAccount != null && currentLocalAccount.getId() == localAccount.getId());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package it.niedermann.owncloud.notes.manageaccounts;

import android.graphics.Color;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.view.Menu;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.PopupMenu;
import androidx.core.util.Consumer;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;

import java.util.stream.Stream;

import it.niedermann.nextcloud.sso.glide.SingleSignOnUrl;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.databinding.ItemAccountChooseBinding;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.shared.model.ApiVersion;

import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static it.niedermann.owncloud.notes.branding.BrandingUtil.applyBrandToLayerDrawable;
import static it.niedermann.owncloud.notes.shared.util.ApiVersionUtil.getPreferredApiVersion;

public class ManageAccountViewHolder extends RecyclerView.ViewHolder {

Expand All @@ -31,7 +35,14 @@ public ManageAccountViewHolder(@NonNull View itemView) {
binding = ItemAccountChooseBinding.bind(itemView);
}

public void bind(@NonNull Account localAccount, @NonNull Consumer<Account> onAccountClick, @Nullable Consumer<Account> onAccountDelete, boolean isCurrentAccount) {
public void bind(
@NonNull Account localAccount,
@NonNull Consumer<Account> onAccountClick,
@NonNull Consumer<Account> onAccountDelete,
@NonNull Consumer<Account> onChangeNotesPath,
@NonNull Consumer<Account> onChangeFileSuffix,
boolean isCurrentAccount
) {
binding.accountName.setText(localAccount.getUserName());
binding.accountHost.setText(Uri.parse(localAccount.getUrl()).getHost());
Glide.with(itemView.getContext())
Expand All @@ -40,12 +51,33 @@ public void bind(@NonNull Account localAccount, @NonNull Consumer<Account> onAcc
.apply(RequestOptions.circleCropTransform())
.into(binding.accountItemAvatar);
itemView.setOnClickListener((v) -> onAccountClick.accept(localAccount));
if (onAccountDelete == null) {
binding.delete.setVisibility(GONE);
} else {
binding.delete.setVisibility(VISIBLE);
binding.delete.setOnClickListener((v) -> onAccountDelete.accept(localAccount));
}
binding.accountContextMenu.setVisibility(VISIBLE);
binding.accountContextMenu.setOnClickListener((v) -> {
final PopupMenu popup = new PopupMenu(itemView.getContext(), v);
popup.inflate(R.menu.menu_account);
final ApiVersion preferredApiVersion = getPreferredApiVersion(localAccount.getApiVersion());
if (preferredApiVersion != null && !preferredApiVersion.supportsSettings()) {
final Menu menu = popup.getMenu();
Stream.of(
R.id.notes_path,
R.id.file_suffix
).forEach((i) -> menu.removeItem(menu.findItem(i).getItemId()));
}
popup.setOnMenuItemClickListener(item -> {
if (item.getItemId() == R.id.notes_path) {
onChangeNotesPath.accept(localAccount);
return true;
} else if (item.getItemId() == R.id.file_suffix) {
onChangeFileSuffix.accept(localAccount);
return true;
} else if (item.getItemId() == R.id.delete) {
onAccountDelete.accept(localAccount);
return true;
}
return false;
});
popup.show();
});
if (isCurrentAccount) {
binding.currentAccountIndicator.setVisibility(VISIBLE);
applyBrandToLayerDrawable((LayerDrawable) binding.currentAccountIndicator.getDrawable(), R.id.area, localAccount.getColor());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
package it.niedermann.owncloud.notes.manageaccounts;

import android.accounts.NetworkErrorException;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.Toast;

import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider;

import com.nextcloud.android.sso.AccountImporter;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;

import it.niedermann.owncloud.notes.LockedActivity;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.BrandedAlertDialogBuilder;
import it.niedermann.owncloud.notes.branding.BrandedDeleteAlertDialogBuilder;
import it.niedermann.owncloud.notes.databinding.ActivityManageAccountsBinding;
import it.niedermann.owncloud.notes.exception.ExceptionDialogFragment;
import it.niedermann.owncloud.notes.persistence.NotesRepository;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
import it.niedermann.owncloud.notes.shared.model.NotesSettings;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
import static it.niedermann.owncloud.notes.shared.util.ApiVersionUtil.getPreferredApiVersion;

public class ManageAccountsActivity extends LockedActivity {

Expand All @@ -30,7 +55,12 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);

adapter = new ManageAccountAdapter(this::selectAccount, this::deleteAccount);
adapter = new ManageAccountAdapter(
this::selectAccount,
this::deleteAccount,
this::onChangeNotesPath,
this::onChangeFileSuffix
);
binding.accounts.setAdapter(adapter);

viewModel.getAccounts$().observe(this, (accounts) -> {
Expand Down Expand Up @@ -83,6 +113,169 @@ public void onError(@NonNull Throwable t) {
});
}

private void onChangeNotesPath(@NonNull Account localAccount) {
final NotesRepository repository = NotesRepository.getInstance(getApplicationContext());
final EditText editText = new EditText(this);
final ViewGroup wrapper = createDialogViewWrapper();
final AlertDialog dialog = new BrandedAlertDialogBuilder(this)
.setTitle(R.string.settings_notes_path)
.setMessage(R.string.settings_notes_path_description)
.setView(wrapper)
.setNeutralButton(android.R.string.cancel, null)
.setPositiveButton(R.string.action_edit_save, (v, d) -> new Thread(() -> {
try {
final Call<NotesSettings> putSettingsCall = repository.putServerSettings(AccountImporter.getSingleSignOnAccount(this, localAccount.getAccountName()), new NotesSettings(editText.getText().toString(), null), getPreferredApiVersion(localAccount.getApiVersion()));
putSettingsCall.enqueue(new Callback<NotesSettings>() {
@Override
public void onResponse(@NonNull Call<NotesSettings> call, @NonNull Response<NotesSettings> response) {
final NotesSettings body = response.body();
if (response.isSuccessful() && body != null) {
runOnUiThread(() -> Toast.makeText(ManageAccountsActivity.this, getString(R.string.settings_notes_path_success, body.getNotesPath()), Toast.LENGTH_LONG).show());
} else {
runOnUiThread(() -> Toast.makeText(ManageAccountsActivity.this, getString(R.string.http_status_code, response.code()), Toast.LENGTH_LONG).show());
}
}

@Override
public void onFailure(@NonNull Call<NotesSettings> call, @NonNull Throwable t) {
runOnUiThread(() -> ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()));
}
});
} catch (NextcloudFilesAppAccountNotFoundException e) {
ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
}
}).start())
.show();
try {
repository.getServerSettings(AccountImporter.getSingleSignOnAccount(this, localAccount.getAccountName()), getPreferredApiVersion(localAccount.getApiVersion()))
.enqueue(new Callback<NotesSettings>() {
@Override
public void onResponse(@NonNull Call<NotesSettings> call, @NonNull Response<NotesSettings> response) {
runOnUiThread(() -> {
final NotesSettings body = response.body();
if (response.isSuccessful() && body != null) {
wrapper.removeAllViews();
final EditText editText = new EditText(ManageAccountsActivity.this);
editText.setText(body.getNotesPath());
wrapper.addView(editText);
} else {
dialog.dismiss();
ExceptionDialogFragment.newInstance(new NetworkErrorException(getString(R.string.http_status_code, response.code()))).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
}
});
}

@Override
public void onFailure(@NonNull Call<NotesSettings> call, @NonNull Throwable t) {
runOnUiThread(() -> {
dialog.dismiss();
ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
});
}
});
} catch (NextcloudFilesAppAccountNotFoundException e) {
dialog.dismiss();
ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
}
}

private void onChangeFileSuffix(@NonNull Account localAccount) {
final NotesRepository repository = NotesRepository.getInstance(getApplicationContext());
final Spinner spinner = new Spinner(this);
final ViewGroup wrapper = createDialogViewWrapper();
final ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.settings_file_suffixes, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
final AlertDialog dialog = new BrandedAlertDialogBuilder(this)
.setTitle(R.string.settings_file_suffix)
.setMessage(R.string.settings_file_suffix_description)
.setView(wrapper)
.setNeutralButton(android.R.string.cancel, null)
.setPositiveButton(R.string.action_edit_save, (v, d) -> new Thread(() -> {
try {
final Call<NotesSettings> putSettingsCall = repository.putServerSettings(AccountImporter.getSingleSignOnAccount(this, localAccount.getAccountName()), new NotesSettings(null, spinner.getSelectedItem().toString()), getPreferredApiVersion(localAccount.getApiVersion()));
putSettingsCall.enqueue(new Callback<NotesSettings>() {
@Override
public void onResponse(@NonNull Call<NotesSettings> call, @NonNull Response<NotesSettings> response) {
final NotesSettings body = response.body();
if (response.isSuccessful() && body != null) {
runOnUiThread(() -> Toast.makeText(ManageAccountsActivity.this, getString(R.string.settings_file_suffix_success, body.getNotesPath()), Toast.LENGTH_LONG).show());
} else {
runOnUiThread(() -> Toast.makeText(ManageAccountsActivity.this, getString(R.string.http_status_code, response.code()), Toast.LENGTH_LONG).show());
}
}

@Override
public void onFailure(@NonNull Call<NotesSettings> call, @NonNull Throwable t) {
runOnUiThread(() -> ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()));
}
});
} catch (NextcloudFilesAppAccountNotFoundException e) {
runOnUiThread(() -> ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()));
}
}).start())
.show();
try {
repository.getServerSettings(AccountImporter.getSingleSignOnAccount(this, localAccount.getAccountName()), getPreferredApiVersion(localAccount.getApiVersion()))
.enqueue(new Callback<NotesSettings>() {
@Override
public void onResponse(@NonNull Call<NotesSettings> call, @NonNull Response<NotesSettings> response) {
final NotesSettings body = response.body();
runOnUiThread(() -> {
if (response.isSuccessful() && body != null) {
for (int i = 0; i < adapter.getCount(); i++) {
if (adapter.getItem(i).equals(body.getFileSuffix())) {
spinner.setSelection(i);
break;
}
}
wrapper.removeAllViews();
wrapper.addView(spinner);
} else {
dialog.dismiss();
ExceptionDialogFragment.newInstance(new Exception(getString(R.string.http_status_code, response.code()))).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
}
});
}

@Override
public void onFailure(@NonNull Call<NotesSettings> call, @NonNull Throwable t) {
runOnUiThread(() -> {
dialog.dismiss();
ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
});
}
});
} catch (NextcloudFilesAppAccountNotFoundException e) {
dialog.dismiss();
ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
}
}

@NonNull
private ViewGroup createDialogViewWrapper() {
final ProgressBar progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
progressBar.setIndeterminate(true);
final FrameLayout wrapper = new FrameLayout(this);
final int paddingVertical = getResources().getDimensionPixelSize(R.dimen.spacer_1x);
final int paddingHorizontal = SDK_INT >= LOLLIPOP_MR1
? getDimensionFromAttribute(android.R.attr.dialogPreferredPadding)
: getResources().getDimensionPixelSize(R.dimen.spacer_2x);
wrapper.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical);
wrapper.addView(progressBar);
return wrapper;
}

@Px
private int getDimensionFromAttribute(@SuppressWarnings("SameParameterValue") @AttrRes int attr) {
final TypedValue typedValue = new TypedValue();
if (getTheme().resolveAttribute(attr, typedValue, true))
return TypedValue.complexToDimensionPixelSize(typedValue.data, getResources().getDisplayMetrics());
else {
return 0;
}
}

@Override
public void applyBrand(int mainColor, int textColor) {
applyBrandToPrimaryToolbar(binding.appBar, binding.toolbar);
Expand Down
Loading