diff options
author | Stefan Niedermann <info@niedermann.it> | 2020-06-07 11:55:29 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2020-06-07 11:55:29 +0300 |
commit | 83f1e06c0f26164e90d74491fc6a5d6772d12efe (patch) | |
tree | cba0f232ea66a28b7bff14aaee985e5a71f537ec /app/src/main | |
parent | c872320b9b530cd7a32af80904ba8bd3455a77f6 (diff) | |
parent | ced0e1a0ecbfb73fa1c4a775951d37d1cd3277ff (diff) |
Merge branch 'master' into api-v1
# Conflicts:
# app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java
# app/src/main/res/values/strings.xml
Diffstat (limited to 'app/src/main')
107 files changed, 2647 insertions, 875 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6180a1a6..fc42c3cf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,12 @@ android:label="@string/app_name" /> <activity + android:name=".manageaccounts.ManageAccountsActivity" + android:label="@string/manage_accounts" + android:parentActivityName=".android.activity.NotesListViewActivity" + android:windowSoftInputMode="stateHidden" /> + + <activity android:name=".android.activity.PreferencesActivity" android:label="@string/action_settings" android:parentActivityName=".android.activity.NotesListViewActivity" diff --git a/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherAdapter.java new file mode 100644 index 00000000..0223a53f --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherAdapter.java @@ -0,0 +1,54 @@ +package it.niedermann.owncloud.notes.accountswitcher; + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.core.util.Consumer; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.model.LocalAccount; + +public class AccountSwitcherAdapter extends RecyclerView.Adapter<AccountSwitcherViewHolder> { + + @NonNull + private final List<LocalAccount> localAccounts = new ArrayList<>(); + @NonNull + private final Consumer<LocalAccount> onAccountClick; + + public AccountSwitcherAdapter(@NonNull Consumer<LocalAccount> onAccountClick) { + this.onAccountClick = onAccountClick; + setHasStableIds(true); + } + + @Override + public long getItemId(int position) { + return localAccounts.get(position).getId(); + } + + @NonNull + @Override + public AccountSwitcherViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new AccountSwitcherViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_account_choose, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull AccountSwitcherViewHolder holder, int position) { + holder.bind(localAccounts.get(position), onAccountClick); + } + + @Override + public int getItemCount() { + return localAccounts.size(); + } + + public void setLocalAccounts(@NonNull List<LocalAccount> localAccounts) { + this.localAccounts.clear(); + this.localAccounts.addAll(localAccounts); + notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java new file mode 100644 index 00000000..249d2c3d --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java @@ -0,0 +1,115 @@ +package it.niedermann.owncloud.notes.accountswitcher; + +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.LayerDrawable; +import android.net.Uri; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; + +import java.util.List; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandedDialogFragment; +import it.niedermann.owncloud.notes.databinding.DialogAccountSwitcherBinding; +import it.niedermann.owncloud.notes.manageaccounts.ManageAccountsActivity; +import it.niedermann.owncloud.notes.model.LocalAccount; +import it.niedermann.owncloud.notes.persistence.NotesDatabase; + +import static it.niedermann.owncloud.notes.android.activity.NotesListViewActivity.manage_account; +import static it.niedermann.owncloud.notes.branding.BrandingUtil.applyBrandToLayerDrawable; + +public class AccountSwitcherDialog extends BrandedDialogFragment { + + private static final String KEY_CURRENT_ACCOUNT_ID = "current_account_id"; + + private NotesDatabase db; + private DialogAccountSwitcherBinding binding; + private AccountSwitcherListener accountSwitcherListener; + private long currentAccountId; + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + if (context instanceof AccountSwitcherListener) { + this.accountSwitcherListener = (AccountSwitcherListener) context; + } else { + throw new ClassCastException("Caller must implement " + AccountSwitcherListener.class.getSimpleName()); + } + + final Bundle args = getArguments(); + + if (args == null || !args.containsKey(KEY_CURRENT_ACCOUNT_ID)) { + throw new IllegalArgumentException("Please provide at least " + KEY_CURRENT_ACCOUNT_ID); + } else { + this.currentAccountId = args.getLong(KEY_CURRENT_ACCOUNT_ID); + } + + db = NotesDatabase.getInstance(getActivity()); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + binding = DialogAccountSwitcherBinding.inflate(requireActivity().getLayoutInflater()); + + LocalAccount currentLocalAccount = db.getAccount(currentAccountId); + binding.accountItemLabel.setText(currentLocalAccount.getAccountName()); + Glide.with(requireContext()) + .load(currentLocalAccount.getUrl() + "/index.php/avatar/" + Uri.encode(currentLocalAccount.getUserName()) + "/64") + .error(R.drawable.ic_account_circle_grey_24dp) + .apply(RequestOptions.circleCropTransform()) + .into(binding.currentAccountItemAvatar); + binding.accountLayout.setOnClickListener((v) -> dismiss()); + + AccountSwitcherAdapter adapter = new AccountSwitcherAdapter((localAccount -> { + accountSwitcherListener.onAccountChosen(localAccount); + dismiss(); + })); + binding.accountsList.setAdapter(adapter); + List<LocalAccount> localAccounts = db.getAccounts(); + for (LocalAccount localAccount : localAccounts) { + if (localAccount.getId() == currentLocalAccount.getId()) { + localAccounts.remove(localAccount); + break; + } + } + adapter.setLocalAccounts(localAccounts); + + binding.addAccount.setOnClickListener((v) -> { + accountSwitcherListener.addAccount(); + dismiss(); + }); + + binding.manageAccounts.setOnClickListener((v) -> { + requireActivity().startActivityForResult(new Intent(requireContext(), ManageAccountsActivity.class), manage_account); + dismiss(); + }); + + return new AlertDialog.Builder(requireContext()) + .setView(binding.getRoot()) + .create(); + } + + public static DialogFragment newInstance(long currentAccountId) { + DialogFragment dialog = new AccountSwitcherDialog(); + + Bundle args = new Bundle(); + args.putLong(KEY_CURRENT_ACCOUNT_ID, currentAccountId); + dialog.setArguments(args); + + return dialog; + } + + @Override + public void applyBrand(int mainColor, int textColor) { + applyBrandToLayerDrawable((LayerDrawable) binding.check.getDrawable(), R.id.area, mainColor); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherListener.java b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherListener.java new file mode 100644 index 00000000..2e26277f --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherListener.java @@ -0,0 +1,11 @@ +package it.niedermann.owncloud.notes.accountswitcher; + +import it.niedermann.owncloud.notes.model.LocalAccount; + +public interface AccountSwitcherListener { + void addAccount(); + + void onAccountChosen(LocalAccount localAccount); + + void onAccountDeleted(LocalAccount localAccount); +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherViewHolder.java new file mode 100644 index 00000000..7093e651 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherViewHolder.java @@ -0,0 +1,36 @@ +package it.niedermann.owncloud.notes.accountswitcher; + +import android.net.Uri; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.core.util.Consumer; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.databinding.ItemAccountChooseBinding; +import it.niedermann.owncloud.notes.model.LocalAccount; + +public class AccountSwitcherViewHolder extends RecyclerView.ViewHolder { + + ItemAccountChooseBinding binding; + + public AccountSwitcherViewHolder(@NonNull View itemView) { + super(itemView); + binding = ItemAccountChooseBinding.bind(itemView); + } + + public void bind(@NonNull LocalAccount localAccount, @NonNull Consumer<LocalAccount> onAccountClick) { + binding.accountItemLabel.setText(localAccount.getAccountName()); + Glide.with(itemView.getContext()) + .load(localAccount.getUrl() + "/index.php/avatar/" + Uri.encode(localAccount.getUserName()) + "/64") + .error(R.drawable.ic_account_circle_grey_24dp) + .apply(RequestOptions.circleCropTransform()) + .into(binding.accountItemAvatar); + itemView.setOnClickListener((v) -> onAccountClick.accept(localAccount)); + binding.delete.setVisibility(View.GONE); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/MultiSelectedActionModeCallback.java b/app/src/main/java/it/niedermann/owncloud/notes/android/MultiSelectedActionModeCallback.java index 499aa212..b90be5d7 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/MultiSelectedActionModeCallback.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/MultiSelectedActionModeCallback.java @@ -1,14 +1,18 @@ package it.niedermann.owncloud.notes.android; import android.content.Context; +import android.graphics.drawable.Drawable; import android.text.TextUtils; +import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import androidx.annotation.ColorInt; import androidx.appcompat.view.ActionMode; import androidx.appcompat.view.ActionMode.Callback; import androidx.appcompat.widget.SearchView; +import androidx.core.graphics.drawable.DrawableCompat; import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.RecyclerView; @@ -23,7 +27,8 @@ import java.util.List; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.activity.NotesListViewActivity; -import it.niedermann.owncloud.notes.android.fragment.AccountChooserDialogFragment; +import it.niedermann.owncloud.notes.android.fragment.MoveAccountDialogFragment; +import it.niedermann.owncloud.notes.branding.BrandedSnackbar; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.ItemAdapter; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper.ViewProvider; @@ -32,6 +37,9 @@ import it.niedermann.owncloud.notes.util.ShareUtil; public class MultiSelectedActionModeCallback implements Callback { + @ColorInt + private int colorAccent; + private final Context context; private final ViewProvider viewProvider; private final NotesDatabase db; @@ -51,12 +59,24 @@ public class MultiSelectedActionModeCallback implements Callback { this.refreshLists = refreshLists; this.fragmentManager = fragmentManager; this.searchView = searchView; + + final TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorAccent, typedValue, true); + colorAccent = typedValue.data; } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { // inflate contextual menu mode.getMenuInflater().inflate(R.menu.menu_list_context_multiple, menu); + for (int i = 0; i < menu.size(); i++) { + Drawable drawable = menu.getItem(i).getIcon(); + if (drawable != null) { + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable, colorAccent); + menu.getItem(i).setIcon(drawable); + } + } return true; } @@ -90,7 +110,7 @@ public class MultiSelectedActionModeCallback implements Callback { String deletedSnackbarTitle = deletedNotes.size() == 1 ? context.getString(R.string.action_note_deleted, deletedNotes.get(0).getTitle()) : context.getString(R.string.bulk_notes_deleted, deletedNotes.size()); - Snackbar.make(viewProvider.getView(), deletedSnackbarTitle, Snackbar.LENGTH_LONG) + BrandedSnackbar.make(viewProvider.getView(), deletedSnackbarTitle, Snackbar.LENGTH_LONG) .setAction(R.string.action_undo, (View v) -> { db.getNoteServerSyncHelper().addCallbackPush(ssoAccount, refreshLists::run); for (DBNote deletedNote : deletedNotes) { @@ -100,7 +120,7 @@ public class MultiSelectedActionModeCallback implements Callback { String restoreSnackbarTitle = deletedNotes.size() == 1 ? context.getString(R.string.action_note_restored, deletedNotes.get(0).getTitle()) : context.getString(R.string.bulk_notes_restored, deletedNotes.size()); - Snackbar.make(viewProvider.getView(), restoreSnackbarTitle, Snackbar.LENGTH_SHORT) + BrandedSnackbar.make(viewProvider.getView(), restoreSnackbarTitle, Snackbar.LENGTH_SHORT) .show(); }) .show(); @@ -109,7 +129,7 @@ public class MultiSelectedActionModeCallback implements Callback { } return true; case R.id.menu_move: - AccountChooserDialogFragment.newInstance().show(fragmentManager, NotesListViewActivity.class.getSimpleName()); + MoveAccountDialogFragment.newInstance().show(fragmentManager, NotesListViewActivity.class.getSimpleName()); return true; case R.id.menu_share: final String subject = (adapter.getSelected().size() == 1) diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java index ccb3cd9c..9ec2eac3 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java @@ -16,9 +16,12 @@ import com.google.android.material.snackbar.Snackbar; import com.nextcloud.android.sso.model.SingleSignOnAccount; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandedSnackbar; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.model.ItemAdapter; +import it.niedermann.owncloud.notes.model.NoteViewHolder; +import it.niedermann.owncloud.notes.model.SectionViewHolder; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper.ViewProvider; import it.niedermann.owncloud.notes.persistence.NotesDatabase; @@ -53,7 +56,7 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper { */ @Override public int getSwipeDirs(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { - if (viewHolder instanceof ItemAdapter.SectionViewHolder) return 0; + if (viewHolder instanceof SectionViewHolder) return 0; return super.getSwipeDirs(recyclerView, viewHolder); } @@ -76,12 +79,12 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper { if (viewProvider == null) { Toast.makeText(context, context.getString(R.string.action_note_deleted, dbNote.getTitle()), Toast.LENGTH_LONG).show(); } else { - Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_deleted, dbNote.getTitle()), Snackbar.LENGTH_LONG) + BrandedSnackbar.make(viewProvider.getView(), context.getString(R.string.action_note_deleted, dbNote.getTitle()), Snackbar.LENGTH_LONG) .setAction(R.string.action_undo, (View v) -> { db.getNoteServerSyncHelper().addCallbackPush(ssoAccount, refreshLists::run); db.addNoteAndSync(ssoAccount, dbNote.getAccountId(), dbNote); refreshLists.run(); - Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_restored, dbNote.getTitle()), Snackbar.LENGTH_SHORT) + BrandedSnackbar.make(viewProvider.getView(), context.getString(R.string.action_note_restored, dbNote.getTitle()), Snackbar.LENGTH_SHORT) .show(); }) .show(); @@ -99,11 +102,11 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper { @Override public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { - ItemAdapter.NoteViewHolder noteViewHolder = (ItemAdapter.NoteViewHolder) viewHolder; + NoteViewHolder noteViewHolder = (NoteViewHolder) viewHolder; // show swipe icon on the side noteViewHolder.showSwipe(dX > 0); // move only swipeable part of item (not leave-behind) - getDefaultUIUtil().onDraw(c, recyclerView, noteViewHolder.noteSwipeable, dX, dY, actionState, isCurrentlyActive); + getDefaultUIUtil().onDraw(c, recyclerView, noteViewHolder.getNoteSwipeable(), dX, dY, actionState, isCurrentlyActive); } @Override @@ -122,7 +125,7 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper { if (swipeRefreshLayout != null) { swipeRefreshLayout.setEnabled(swipeRefreshLayoutEnabled); } - getDefaultUIUtil().clearView(((ItemAdapter.NoteViewHolder) viewHolder).noteSwipeable); + getDefaultUIUtil().clearView(((NoteViewHolder) viewHolder).getNoteSwipeable()); } @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AboutActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AboutActivity.java index 0334ba64..d6cd1c39 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AboutActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AboutActivity.java @@ -2,6 +2,7 @@ package it.niedermann.owncloud.notes.android.activity; import android.os.Bundle; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -11,15 +12,18 @@ import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.fragment.about.AboutFragmentContributingTab; import it.niedermann.owncloud.notes.android.fragment.about.AboutFragmentCreditsTab; import it.niedermann.owncloud.notes.android.fragment.about.AboutFragmentLicenseTab; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.ActivityAboutBinding; public class AboutActivity extends LockedActivity { + private ActivityAboutBinding binding; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActivityAboutBinding binding = ActivityAboutBinding.inflate(getLayoutInflater()); + binding = ActivityAboutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); @@ -27,6 +31,12 @@ public class AboutActivity extends LockedActivity { binding.tabs.setupWithViewPager(binding.pager); } + @Override + public void applyBrand(int mainColor, int textColor) { + @ColorInt int finalMainColor = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(this, mainColor); + binding.tabs.setSelectedTabIndicatorColor(finalMainColor); + } + private class TabsPagerAdapter extends FragmentPagerAdapter { TabsPagerAdapter(FragmentManager fragmentManager) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java index 3f286c1c..2cc72aea 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java @@ -2,9 +2,13 @@ package it.niedermann.owncloud.notes.android.activity; import android.content.Intent; import android.os.Bundle; +import android.util.Log; import android.view.View; import android.widget.Toast; +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; + import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.model.DBNote; @@ -20,7 +24,12 @@ public class AppendToNoteActivity extends NotesListViewActivity { super.onCreate(savedInstanceState); final Intent receivedIntent = getIntent(); receivedText = receivedIntent.getStringExtra(Intent.EXTRA_TEXT); - binding.activityNotesListView.toolbar.setTitle(R.string.append_to_note); + @Nullable final ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setTitle(R.string.append_to_note); + } else { + Log.e(TAG, "SupportActioBar is null. Expected toolbar to be present to set a title."); + } binding.activityNotesListView.toolbar.setSubtitle(receivedText); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java index d9e9fb7d..6e6fc2e6 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java @@ -29,9 +29,9 @@ import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.util.NoteUtil; -import static it.niedermann.owncloud.notes.android.fragment.AccountChooserAdapter.AccountChooserListener; +import static it.niedermann.owncloud.notes.android.fragment.AccountChooserAdapter.MoveAccountListener; -public class EditNoteActivity extends LockedActivity implements BaseNoteFragment.NoteFragmentListener, AccountChooserListener { +public class EditNoteActivity extends LockedActivity implements BaseNoteFragment.NoteFragmentListener, MoveAccountListener { private static final String TAG = EditNoteActivity.class.getSimpleName(); @@ -54,6 +54,7 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment binding = ActivityEditBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); + setSupportActionBar(binding.toolbar); if (savedInstanceState == null) { launchNoteFragment(); @@ -267,7 +268,12 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment } @Override - public void onAccountChosen(LocalAccount account) { + public void moveToAccount(LocalAccount account) { fragment.moveNote(account); } + + @Override + public void applyBrand(int mainColor, int textColor) { + applyBrandToPrimaryToolbar(binding.toolbar); + } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/LockedActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/LockedActivity.java index 0b0ca7c1..07a544df 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/LockedActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/LockedActivity.java @@ -8,13 +8,13 @@ import android.os.Bundle; import android.util.Log; import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; import it.niedermann.owncloud.notes.ExceptionHandler; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandedActivity; import it.niedermann.owncloud.notes.util.Notes; -public abstract class LockedActivity extends AppCompatActivity { +public abstract class LockedActivity extends BrandedActivity { private static final String TAG = LockedActivity.class.getSimpleName(); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NoteListWidgetConfigurationActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NoteListWidgetConfigurationActivity.java index eefe770a..92f20c4b 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NoteListWidgetConfigurationActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NoteListWidgetConfigurationActivity.java @@ -82,7 +82,7 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity { RecyclerView recyclerView; RecyclerView.LayoutManager layoutManager; - adapterCategories = new NavigationAdapter(new NavigationAdapter.ClickListener() { + adapterCategories = new NavigationAdapter(this, new NavigationAdapter.ClickListener() { @Override public void onItemClick(NavigationAdapter.NavigationItem item) { NoteListsWidgetData data = new NoteListsWidgetData(); @@ -136,6 +136,10 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity { new LoadCategoryListTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + @Override + public void applyBrand(int mainColor, int textColor) { + } + private class LoadCategoryListTask extends AsyncTask<Void, Void, List<NavigationAdapter.NavigationItem>> { @Override protected List<NavigationAdapter.NavigationItem> doInBackground(Void... voids) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java index 447006e8..6efa0ce6 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java @@ -1,31 +1,30 @@ package it.niedermann.owncloud.notes.android.activity; +import android.animation.AnimatorInflater; +import android.annotation.SuppressLint; import android.app.SearchManager; import android.content.Intent; -import android.content.res.Configuration; -import android.database.sqlite.SQLiteConstraintException; +import android.database.sqlite.SQLiteException; +import android.graphics.Color; +import android.graphics.PorterDuff; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.text.TextUtils; import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.view.ViewTreeObserver; -import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.view.ActionMode; -import androidx.appcompat.widget.AppCompatImageView; import androidx.appcompat.widget.SearchView; -import androidx.appcompat.widget.Toolbar; import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.view.GravityCompat; +import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -39,6 +38,7 @@ import com.nextcloud.android.sso.exceptions.AccountImportCancelledException; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; +import com.nextcloud.android.sso.exceptions.TokenMismatchException; import com.nextcloud.android.sso.helper.SingleAccountHelper; import com.nextcloud.android.sso.model.SingleSignOnAccount; @@ -48,10 +48,15 @@ import java.util.List; import java.util.Map; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.accountswitcher.AccountSwitcherDialog; +import it.niedermann.owncloud.notes.accountswitcher.AccountSwitcherListener; import it.niedermann.owncloud.notes.android.MultiSelectedActionModeCallback; import it.niedermann.owncloud.notes.android.NotesListViewItemTouchHelper; -import it.niedermann.owncloud.notes.android.fragment.AccountChooserAdapter.AccountChooserListener; +import it.niedermann.owncloud.notes.android.fragment.AccountChooserAdapter.MoveAccountListener; import it.niedermann.owncloud.notes.android.fragment.ExceptionDialogFragment; +import it.niedermann.owncloud.notes.branding.BrandedSnackbar; +import it.niedermann.owncloud.notes.branding.BrandingUtil; +import it.niedermann.owncloud.notes.databinding.ActivityNotesListViewBinding; import it.niedermann.owncloud.notes.databinding.DrawerLayoutBinding; import it.niedermann.owncloud.notes.model.Capabilities; import it.niedermann.owncloud.notes.model.Category; @@ -63,17 +68,23 @@ import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.model.NavigationAdapter; import it.niedermann.owncloud.notes.model.NavigationAdapter.CategoryNavigationItem; import it.niedermann.owncloud.notes.model.NavigationAdapter.NavigationItem; +import it.niedermann.owncloud.notes.model.NoteClickListener; import it.niedermann.owncloud.notes.persistence.CapabilitiesClient; import it.niedermann.owncloud.notes.persistence.CapabilitiesWorker; import it.niedermann.owncloud.notes.persistence.LoadNotesListTask; import it.niedermann.owncloud.notes.persistence.LoadNotesListTask.NotesLoadedListener; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper; +import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper.ViewProvider; import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.util.NoteUtil; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static it.niedermann.owncloud.notes.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.owncloud.notes.util.ColorUtil.contrastRatioIsSufficient; import static it.niedermann.owncloud.notes.util.SSOUtil.askForNewAccount; -public class NotesListViewActivity extends LockedActivity implements ItemAdapter.NoteClickListener, NoteServerSyncHelper.ViewProvider, AccountChooserListener { +public class NotesListViewActivity extends LockedActivity implements NoteClickListener, ViewProvider, MoveAccountListener, AccountSwitcherListener { private static final String TAG = NotesListViewActivity.class.getSimpleName(); @@ -91,6 +102,7 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter private final static int show_single_note_cmd = 1; private final static int server_settings = 2; private final static int about = 3; + public final static int manage_account = 4; /** * Used to detect the onResume() call after the import dialog has been displayed. @@ -102,6 +114,7 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter protected LocalAccount localAccount; protected DrawerLayoutBinding binding; + protected ActivityNotesListViewBinding activityBinding; private CoordinatorLayout coordinatorLayout; private SwipeRefreshLayout swipeRefreshLayout; @@ -111,15 +124,14 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter protected ItemAdapter adapter = null; protected NotesDatabase db = null; - private ActionBarDrawerToggle drawerToggle; private NavigationAdapter adapterCategories; private NavigationItem itemRecent; private NavigationItem itemFavorites; private NavigationItem itemUncategorized; + @NonNull private Category navigationSelection = new Category(null, null); private String navigationOpen = ""; private ActionMode mActionMode; - private SearchView searchView = null; private final ISyncCallback syncCallBack = () -> { adapter.clearSelection(listView); if (mActionMode != null) { @@ -128,7 +140,6 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter refreshLists(); swipeRefreshLayout.setRefreshing(false); }; - private boolean accountChooserActive; @Override protected void onCreate(Bundle savedInstanceState) { @@ -136,7 +147,10 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter CapabilitiesWorker.update(this); binding = DrawerLayoutBinding.inflate(getLayoutInflater()); + activityBinding = ActivityNotesListViewBinding.bind(binding.activityNotesListView.getRoot()); + setContentView(binding.getRoot()); + this.coordinatorLayout = binding.activityNotesListView.activityNotesListView; this.swipeRefreshLayout = binding.activityNotesListView.swiperefreshlayout; this.fabCreate = binding.activityNotesListView.fabCreate; @@ -151,15 +165,17 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter navigationSelection = new Category(null, true); } } else { - navigationSelection = (Category) savedInstanceState.getSerializable(SAVED_STATE_NAVIGATION_SELECTION); + Object savedCategory = savedInstanceState.getSerializable(SAVED_STATE_NAVIGATION_SELECTION); + if (savedCategory != null) { + navigationSelection = (Category) savedCategory; + } navigationOpen = savedInstanceState.getString(SAVED_STATE_NAVIGATION_OPEN); categoryAdapterSelectedItem = savedInstanceState.getString(SAVED_STATE_NAVIGATION_ADAPTER_SLECTION); } db = NotesDatabase.getInstance(this); - setupHeader(); - setupActionBar(); + setupToolbars(); setupNavigationList(categoryAdapterSelectedItem); setupNavigationMenu(); setupNotesList(); @@ -194,18 +210,6 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter } @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - drawerToggle.syncState(); - } - - @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - super.onConfigurationChanged(newConfig); - drawerToggle.syncState(); - } - - @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); if (localAccount != null) { @@ -221,6 +225,7 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter localAccount = db.getLocalAccountByAccountName(accountName); if (localAccount != null) { try { + BrandingUtil.saveBrandColors(this, localAccount.getColor(), localAccount.getTextColor()); ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()); new NotesListViewItemTouchHelper(ssoAccount, this, db, adapter, syncCallBack, this::refreshLists, swipeRefreshLayout, this).attachToRecyclerView(listView); synchronize(); @@ -230,14 +235,21 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter } refreshLists(); fabCreate.show(); - setupHeader(); + activityBinding.launchAccountSwitcher.setOnClickListener((v) -> { + if (localAccount == null) { + handleNotAuthorizedAccount(); + } else { + AccountSwitcherDialog.newInstance(localAccount.getId()).show(getSupportFragmentManager(), AccountSwitcherDialog.class.getSimpleName()); + } + }); setupNavigationList(ADAPTER_KEY_RECENT); - updateUsernameInDrawer(); } else { if (!notAuthorizedAccountHandled) { handleNotAuthorizedAccount(); } + binding.navigationList.setAdapter(null); } + updateCurrentAccountAvatar(); } private void handleNotAuthorizedAccount() { @@ -247,55 +259,59 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter notAuthorizedAccountHandled = true; } - private void setupHeader() { - binding.accountChooser.removeAllViews(); - for (LocalAccount localAccount : db.getAccounts()) { - View v = View.inflate(this, R.layout.item_account, null); - ((TextView) v.findViewById(R.id.accountItemLabel)).setText(localAccount.getAccountName()); - Glide - .with(this) - .load(localAccount.getUrl() + "/index.php/avatar/" + Uri.encode(localAccount.getUserName()) + "/64") - .error(R.drawable.ic_account_circle_grey_24dp) - .apply(RequestOptions.circleCropTransform()) - .into(((ImageView) v.findViewById(R.id.accountItemAvatar))); - v.setOnClickListener(clickedView -> { - clickHeader(); - binding.drawerLayout.closeDrawer(GravityCompat.START); - selectAccount(localAccount.getAccountName()); - }); - v.findViewById(R.id.delete).setOnClickListener(clickedView -> { - db.deleteAccount(localAccount); - if (localAccount.getId() == this.localAccount.getId()) { - List<LocalAccount> remainingAccounts = db.getAccounts(); - if (remainingAccounts.size() > 0) { - this.localAccount = remainingAccounts.get(0); - selectAccount(this.localAccount.getAccountName()); + private void setupToolbars() { + setSupportActionBar(binding.activityNotesListView.toolbar); + updateCurrentAccountAvatar(); + activityBinding.homeToolbar.setOnClickListener((v) -> { + if (activityBinding.toolbar.getVisibility() == GONE) { + updateToolbars(false); + } + }); + + activityBinding.launchAccountSwitcher.setOnClickListener((v) -> askForNewAccount(this)); + activityBinding.menuButton.setOnClickListener((v) -> binding.drawerLayout.openDrawer(GravityCompat.START)); + + final LinearLayout searchEditFrame = activityBinding.searchView.findViewById(R.id + .search_edit_frame); + + searchEditFrame.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + int oldVisibility = -1; + + @Override + public void onGlobalLayout() { + int currentVisibility = searchEditFrame.getVisibility(); + + if (currentVisibility != oldVisibility) { + if (currentVisibility == VISIBLE) { + fabCreate.hide(); } else { - selectAccount(null); - askForNewAccount(this); + new Handler().postDelayed(() -> fabCreate.show(), 150); } + + oldVisibility = currentVisibility; } - setupHeader(); - clickHeader(); - binding.drawerLayout.closeDrawer(GravityCompat.START); - }); - binding.accountChooser.addView(v); - } - View addButton = View.inflate(this, R.layout.item_account, null); - ((TextView) addButton.findViewById(R.id.accountItemLabel)).setText(getString(R.string.add_account)); - ((AppCompatImageView) addButton.findViewById(R.id.accountItemAvatar)).setImageResource(R.drawable.ic_person_add_grey600_24dp); - addButton.setOnClickListener((btn) -> askForNewAccount(this)); - addButton.findViewById(R.id.delete).setVisibility(View.GONE); - binding.accountChooser.addView(addButton); - binding.headerView.setOnClickListener(view -> clickHeader()); - } + } - private void setupActionBar() { - Toolbar toolbar = binding.activityNotesListView.toolbar; - setSupportActionBar(toolbar); - drawerToggle = new ActionBarDrawerToggle(this, binding.drawerLayout, toolbar, R.string.action_drawer_open, R.string.action_drawer_close); - drawerToggle.setDrawerIndicatorEnabled(true); - binding.drawerLayout.addDrawerListener(drawerToggle); + }); + activityBinding.searchView.setOnCloseListener(() -> { + if (activityBinding.toolbar.getVisibility() == VISIBLE && TextUtils.isEmpty(activityBinding.searchView.getQuery())) { + updateToolbars(true); + return true; + } + return false; + }); + activityBinding.searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + refreshLists(); + return true; + } + }); } private void setupNotesList() { @@ -330,6 +346,9 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter db.updateCapabilitiesETag(localAccount.getId(), capabilities.getETag()); db.updateBrand(localAccount.getId(), capabilities); db.updateBrand(localAccount.getId(), capabilities); + localAccount.setColor(Color.parseColor(capabilities.getColor())); + localAccount.setTextColor(Color.parseColor(capabilities.getTextColor())); + BrandingUtil.saveBrandColors(this, localAccount.getColor(), localAccount.getTextColor()); db.updateApiVersion(localAccount.getId(), capabilities.getApiVersion()); Log.i(TAG, capabilities.toString()); } catch (Exception e) { @@ -350,8 +369,8 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter fabCreate.setOnClickListener((View view) -> { Intent createIntent = new Intent(getApplicationContext(), EditNoteActivity.class); createIntent.putExtra(EditNoteActivity.PARAM_CATEGORY, navigationSelection); - if (searchView != null && !searchView.isIconified() && searchView.getQuery().length() > 0) { - createIntent.putExtra(EditNoteActivity.PARAM_CONTENT, searchView.getQuery().toString()); + if (activityBinding.searchView.getQuery().length() > 0) { + createIntent.putExtra(EditNoteActivity.PARAM_CONTENT, activityBinding.searchView.getQuery().toString()); invalidateOptionsMenu(); } startActivityForResult(createIntent, create_note_cmd); @@ -361,7 +380,7 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter private void setupNavigationList(final String selectedItem) { itemRecent = new NavigationItem(ADAPTER_KEY_RECENT, getString(R.string.label_all_notes), null, R.drawable.ic_access_time_grey600_24dp); itemFavorites = new NavigationItem(ADAPTER_KEY_STARRED, getString(R.string.label_favorites), null, R.drawable.ic_star_yellow_24dp); - adapterCategories = new NavigationAdapter(new NavigationAdapter.ClickListener() { + adapterCategories = new NavigationAdapter(this, new NavigationAdapter.ClickListener() { @Override public void onItemClick(NavigationItem item) { selectItem(item, true); @@ -414,21 +433,40 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter binding.navigationList.setAdapter(adapterCategories); } - private void clickHeader() { - if (this.accountChooserActive) { - binding.accountChooser.setVisibility(View.GONE); - binding.accountNavigation.setVisibility(View.VISIBLE); - } else { - binding.accountChooser.setVisibility(View.VISIBLE); - binding.accountNavigation.setVisibility(View.GONE); + @Override + public CoordinatorLayout getView() { + return this.coordinatorLayout; + } + @Override + public void applyBrand(int mainColor, int textColor) { + applyBrandToPrimaryToolbar(activityBinding.toolbar); + applyBrandToFAB(mainColor, textColor, activityBinding.fabCreate); + + binding.headerView.setBackgroundColor(mainColor); + binding.appName.setTextColor(textColor); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activityBinding.progressCircular.getIndeterminateDrawable().setColorFilter(getSecondaryForegroundColorDependingOnTheme(this, mainColor), PorterDuff.Mode.SRC_IN); } - this.accountChooserActive = !this.accountChooserActive; + + // TODO We assume, that the background of the spinner is always white + activityBinding.swiperefreshlayout.setColorSchemeColors(contrastRatioIsSufficient(Color.WHITE, mainColor) ? mainColor : Color.BLACK); + binding.appName.setTextColor(textColor); + DrawableCompat.setTint(binding.logo.getDrawable(), textColor); + + adapter.applyBrand(mainColor, textColor); + adapterCategories.applyBrand(mainColor, textColor); + invalidateOptionsMenu(); } @Override - public CoordinatorLayout getView() { - return this.coordinatorLayout; + public boolean onSupportNavigateUp() { + if (activityBinding.toolbar.getVisibility() == VISIBLE) { + updateToolbars(true); + return true; + } else { + return super.onSupportNavigateUp(); + } } private class LoadCategoryListTask extends AsyncTask<Void, Void, List<NavigationItem>> { @@ -521,12 +559,12 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter final NavigationItem itemSettings = new NavigationItem("settings", getString(R.string.action_settings), null, R.drawable.ic_settings_grey600_24dp); final NavigationItem itemAbout = new NavigationItem("about", getString(R.string.simple_about), null, R.drawable.ic_info_outline_grey600_24dp); - ArrayList<NavigationItem> itemsMenu = new ArrayList<>(); + ArrayList<NavigationItem> itemsMenu = new ArrayList<>(3); itemsMenu.add(itemTrashbin); itemsMenu.add(itemSettings); itemsMenu.add(itemAbout); - NavigationAdapter adapterMenu = new NavigationAdapter(new NavigationAdapter.ClickListener() { + NavigationAdapter adapterMenu = new NavigationAdapter(this, new NavigationAdapter.ClickListener() { @Override public void onItemClick(NavigationItem item) { if (itemSettings.equals(item)) { @@ -545,9 +583,6 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter onItemClick(item); } }); - - - this.updateUsernameInDrawer(); adapterMenu.setItems(itemsMenu); binding.navigationMenu.setAdapter(adapterMenu); } @@ -569,35 +604,36 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter return; } View emptyContentView = binding.activityNotesListView.emptyContentView.getRoot(); - emptyContentView.setVisibility(View.GONE); - binding.activityNotesListView.progressCircular.setVisibility(View.VISIBLE); + emptyContentView.setVisibility(GONE); + binding.activityNotesListView.progressCircular.setVisibility(VISIBLE); fabCreate.show(); String subtitle; if (navigationSelection.category != null) { if (navigationSelection.category.isEmpty()) { - subtitle = getString(R.string.action_uncategorized); + subtitle = getString(R.string.search_in_category, getString(R.string.action_uncategorized)); } else { - subtitle = NoteUtil.extendCategory(navigationSelection.category); + subtitle = getString(R.string.search_in_category, NoteUtil.extendCategory(navigationSelection.category)); } } else if (navigationSelection.favorite != null && navigationSelection.favorite) { - subtitle = getString(R.string.label_favorites); + subtitle = getString(R.string.search_in_category, getString(R.string.label_favorites)); } else { - subtitle = getString(R.string.app_name); + subtitle = getString(R.string.search_in_all); } - setTitle(subtitle); + activityBinding.searchText.setText(subtitle); CharSequence query = null; - if (searchView != null && !searchView.isIconified() && searchView.getQuery().length() != 0) { - query = searchView.getQuery(); + if (activityBinding.searchView.getQuery().length() != 0) { + query = activityBinding.searchView.getQuery(); } - NotesLoadedListener callback = (List<Item> notes, boolean showCategory) -> { + NotesLoadedListener callback = (List<Item> notes, boolean showCategory, CharSequence searchQuery) -> { adapter.setShowCategory(showCategory); + adapter.setHighlightSearchQuery(searchQuery); adapter.setItemList(notes); - binding.activityNotesListView.progressCircular.setVisibility(View.GONE); + binding.activityNotesListView.progressCircular.setVisibility(GONE); if (notes.size() > 0) { - emptyContentView.setVisibility(View.GONE); + emptyContentView.setVisibility(GONE); } else { - emptyContentView.setVisibility(View.VISIBLE); + emptyContentView.setVisibility(VISIBLE); } if (scrollToTop) { listView.scrollToPosition(0); @@ -607,62 +643,10 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter new LoadCategoryListTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - - /** - * Adds the Menu Items to the Action Bar. - * - * @param menu Menu - * @return boolean - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_list_view, menu); - // Associate searchable configuration with the SearchView - final MenuItem item = menu.findItem(R.id.search); - searchView = (SearchView) item.getActionView(); - - final LinearLayout searchEditFrame = searchView.findViewById(R.id - .search_edit_frame); - - searchEditFrame.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - int oldVisibility = -1; - - @Override - public void onGlobalLayout() { - int currentVisibility = searchEditFrame.getVisibility(); - - if (currentVisibility != oldVisibility) { - if (currentVisibility == View.VISIBLE) { - fabCreate.hide(); - } else { - new Handler().postDelayed(() -> fabCreate.show(), 150); - } - - oldVisibility = currentVisibility; - } - } - - }); - - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - return false; - } - - @Override - public boolean onQueryTextChange(String newText) { - refreshLists(); - return true; - } - }); - return true; - } - @Override protected void onNewIntent(Intent intent) { if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - searchView.setQuery(intent.getStringExtra(SearchManager.QUERY), true); + activityBinding.searchView.setQuery(intent.getStringExtra(SearchManager.QUERY), true); } super.onNewIntent(intent); } @@ -684,98 +668,99 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - // Check which request we're responding to - if (requestCode == create_note_cmd) { - // Make sure the request was successful - if (resultCode == RESULT_OK) { - //not need because of db.synchronisation in createActivity - - Bundle bundle = data.getExtras(); - if (bundle != null && bundle.containsKey(CREATED_NOTE)) { - DBNote createdNote = (DBNote) bundle.getSerializable(CREATED_NOTE); - if (createdNote != null) { - adapter.add(createdNote); + switch (requestCode) { + case create_note_cmd: { + // Make sure the request was successful + if (resultCode == RESULT_OK) { + //not need because of db.synchronisation in createActivity + + Bundle bundle = data.getExtras(); + if (bundle != null && bundle.containsKey(CREATED_NOTE)) { + DBNote createdNote = (DBNote) bundle.getSerializable(CREATED_NOTE); + if (createdNote != null) { + adapter.add(createdNote); + } else { + Log.w(TAG, "createdNote must not be null"); + } } else { - Log.w(TAG, "createdNote must not be null"); + Log.w(TAG, "Provide at least " + CREATED_NOTE); } - } else { - Log.w(TAG, "Provide at least " + CREATED_NOTE); } + listView.scrollToPosition(0); + break; } - listView.scrollToPosition(0); - } else if (requestCode == server_settings) { - // Recreate activity completely, because theme switching makes problems when only invalidating the views. - // @see https://github.com/stefan-niedermann/nextcloud-notes/issues/529 - recreate(); - } else { - try { - AccountImporter.onActivityResult(requestCode, resultCode, data, this, (ssoAccount) -> { - CapabilitiesWorker.update(this); - new Thread(() -> { - Log.i(TAG, "Added account: " + "name:" + ssoAccount.name + ", " + ssoAccount.url + ", userId" + ssoAccount.userId); - try { - Log.i(TAG, "Refreshing capabilities for " + ssoAccount.name); - final Capabilities capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, null); - db.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities); - Log.i(TAG, capabilities.toString()); - runOnUiThread(() -> { - selectAccount(ssoAccount.name); - this.accountChooserActive = false; - binding.accountChooser.setVisibility(View.GONE); - binding.accountNavigation.setVisibility(View.VISIBLE); - binding.drawerLayout.closeDrawer(GravityCompat.START); - }); - } catch (SQLiteConstraintException e) { - if (db.getAccounts().size() > 1) { // TODO ideally only show snackbar when this is a not migrated account - runOnUiThread(() -> { - Snackbar.make(coordinatorLayout, R.string.account_already_imported, Snackbar.LENGTH_LONG).show(); - selectAccount(ssoAccount.name); - this.accountChooserActive = false; - binding.accountChooser.setVisibility(View.GONE); - binding.accountNavigation.setVisibility(View.VISIBLE); - binding.drawerLayout.closeDrawer(GravityCompat.START); - }); + case server_settings: { + // Recreate activity completely, because theme switching makes problems when only invalidating the views. + // @see https://github.com/stefan-niedermann/nextcloud-notes/issues/529 + recreate(); + break; + } + case manage_account: { + if (resultCode == RESULT_FIRST_USER) { + selectAccount(null); + } + break; + } + default: { + try { + AccountImporter.onActivityResult(requestCode, resultCode, data, this, (ssoAccount) -> { + CapabilitiesWorker.update(this); + new Thread(() -> { + Log.i(TAG, "Added account: " + "name:" + ssoAccount.name + ", " + ssoAccount.url + ", userId" + ssoAccount.userId); + try { + Log.i(TAG, "Refreshing capabilities for " + ssoAccount.name); + final Capabilities capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, null); + db.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities); + Log.i(TAG, capabilities.toString()); + runOnUiThread(() -> selectAccount(ssoAccount.name)); + } catch (SQLiteException e) { + // Happens when upgrading from version ≤ 1.0.1 and importing the account + runOnUiThread(() -> selectAccount(ssoAccount.name)); + } catch (Exception e) { + // Happens when importing an already existing account the second time + if (e instanceof TokenMismatchException && db.getLocalAccountByAccountName(ssoAccount.name) != null) { + Log.w(TAG, "Received " + TokenMismatchException.class.getSimpleName() + " and the given ssoAccount.name (" + ssoAccount.name + ") does already exist in the database. Assume that this account has already been imported."); + runOnUiThread(() -> { + selectAccount(ssoAccount.name); + // TODO there is already a sync in progress and results in displaying a TokenMissMatchException snackbar which conflicts with this one + coordinatorLayout.post(() -> BrandedSnackbar.make(coordinatorLayout, R.string.account_already_imported, Snackbar.LENGTH_LONG).show()); + }); + } else { + e.printStackTrace(); + runOnUiThread(() -> { + binding.activityNotesListView.progressCircular.setVisibility(GONE); + ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()); + }); + } } - } catch (Exception e) { - e.printStackTrace(); - runOnUiThread(() -> { - this.accountChooserActive = true; - binding.accountChooser.setVisibility(View.VISIBLE); - binding.accountNavigation.setVisibility(View.GONE); - binding.drawerLayout.openDrawer(GravityCompat.START); - binding.activityNotesListView.progressCircular.setVisibility(View.GONE); - ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()); - }); - } - }).start(); - }); - } catch (AccountImportCancelledException e) { - Log.i(TAG, "AccountImport has been cancelled."); + }).start(); + }); + } catch (AccountImportCancelledException e) { + Log.i(TAG, "AccountImport has been cancelled."); + } } } } - private void updateUsernameInDrawer() { + private void updateCurrentAccountAvatar() { try { String url = localAccount.getUrl(); if (url != null) { - binding.account.setText(localAccount.getAccountName()); Glide .with(this) .load(url + "/index.php/avatar/" + Uri.encode(localAccount.getUserName()) + "/64") - .error(R.mipmap.ic_launcher) + .error(R.drawable.ic_account_circle_grey_24dp) .apply(RequestOptions.circleCropTransform()) - .into(binding.currentAccountImage); + .into(activityBinding.launchAccountSwitcher); } else { Log.w(TAG, "url is null"); } } catch (NullPointerException e) { // No local account - show generic header - binding.account.setText(R.string.app_name_long); Glide .with(this) - .load(R.mipmap.ic_launcher) + .load(R.drawable.ic_account_circle_grey_24dp) .apply(RequestOptions.circleCropTransform()) - .into(binding.currentAccountImage); + .into(activityBinding.launchAccountSwitcher); Log.w(TAG, "Tried to update username in drawer, but localAccount was null"); } } @@ -820,7 +805,7 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter if (selected) { v.setSelected(true); mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback( - this, this, db, mActionMode, adapter, listView, this::refreshLists, getSupportFragmentManager(), searchView + this, this, db, mActionMode, adapter, listView, this::refreshLists, getSupportFragmentManager(), activityBinding.searchView )); int checkedItemCount = adapter.getSelected().size(); mActionMode.setTitle(getResources().getQuantityString(R.plurals.ab_selected, checkedItemCount, checkedItemCount)); @@ -830,11 +815,27 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter @Override public void onBackPressed() { - if (searchView == null || searchView.isIconified()) { + if (activityBinding.toolbar.getVisibility() == VISIBLE) { + updateToolbars(true); + } else { super.onBackPressed(); + } + } + + @SuppressLint("PrivateResource") + private void updateToolbars(boolean disableSearch) { + activityBinding.homeToolbar.setVisibility(disableSearch ? VISIBLE : GONE); + activityBinding.toolbar.setVisibility(disableSearch ? GONE : VISIBLE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activityBinding.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(activityBinding.appBar.getContext(), + disableSearch ? R.animator.appbar_elevation_off : R.animator.appbar_elevation_on)); } else { - invalidateOptionsMenu(); + ViewCompat.setElevation(activityBinding.appBar, disableSearch ? 0 : getResources().getDimension(R.dimen.design_appbar_elevation)); } + if (disableSearch) { + activityBinding.searchView.setQuery(null, true); + } + activityBinding.searchView.setIconified(disableSearch); } private void synchronize() { @@ -849,14 +850,39 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter Log.d(TAG, "Network is connected, but sync is not possible"); } else { Log.d(TAG, "Sync is not possible, because network is not connected"); - Snackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show(); + BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show(); } } } + @Override + public void addAccount() { + askForNewAccount(this); + } + + @Override + public void onAccountChosen(LocalAccount localAccount) { + binding.drawerLayout.closeDrawer(GravityCompat.START); + selectAccount(localAccount.getAccountName()); + } + + @Override + public void onAccountDeleted(LocalAccount localAccount) { + db.deleteAccount(localAccount); + if (localAccount.getId() == this.localAccount.getId()) { + List<LocalAccount> remainingAccounts = db.getAccounts(); + if (remainingAccounts.size() > 0) { + this.localAccount = remainingAccounts.get(0); + selectAccount(this.localAccount.getAccountName()); + } else { + selectAccount(null); + askForNewAccount(this); + } + } + } @Override - public void onAccountChosen(LocalAccount account) { + public void moveToAccount(LocalAccount account) { List<Integer> selection = new ArrayList<>(adapter.getSelected()); adapter.deselect(0); @@ -872,7 +898,6 @@ public class NotesListViewActivity extends LockedActivity implements ItemAdapter } mActionMode.finish(); - searchView.setIconified(true); refreshLists(); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/PreferencesActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/PreferencesActivity.java index 3efc1ad5..ec1566b2 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/PreferencesActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/PreferencesActivity.java @@ -14,11 +14,13 @@ import it.niedermann.owncloud.notes.databinding.ActivityPreferencesBinding; public class PreferencesActivity extends LockedActivity { + private ActivityPreferencesBinding binding; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActivityPreferencesBinding binding = ActivityPreferencesBinding.inflate(getLayoutInflater()); + binding = ActivityPreferencesBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); @@ -27,4 +29,9 @@ public class PreferencesActivity extends LockedActivity { .replace(R.id.fragment_container_view, new PreferencesFragment()) .commit(); } + + @Override + public void applyBrand(int mainColor, int textColor) { + applyBrandToPrimaryToolbar(binding.toolbar); + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/AccountChooserAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/AccountChooserAdapter.java index cf20c7b6..135e1426 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/AccountChooserAdapter.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/AccountChooserAdapter.java @@ -26,14 +26,14 @@ public class AccountChooserAdapter extends RecyclerView.Adapter<RecyclerView.Vie @NonNull private final List<LocalAccount> localAccounts; @NonNull - private final AccountChooserListener accountChooserListener; + private final MoveAccountListener moveAccountListener; @NonNull private final Context context; - AccountChooserAdapter(@NonNull List<LocalAccount> localAccounts, @NonNull AccountChooserListener accountChooserListener, @NonNull Context context) { + AccountChooserAdapter(@NonNull List<LocalAccount> localAccounts, @NonNull MoveAccountListener moveAccountListener, @NonNull Context context) { super(); this.localAccounts = localAccounts; - this.accountChooserListener = accountChooserListener; + this.moveAccountListener = moveAccountListener; this.context = context; } @@ -48,7 +48,7 @@ public class AccountChooserAdapter extends RecyclerView.Adapter<RecyclerView.Vie public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { LocalAccount localAccount = localAccounts.get(position); AccountChooserViewHolder accountChooserViewHolder = (AccountChooserViewHolder) holder; - accountChooserViewHolder.getAccountLayout().setOnClickListener((v) -> accountChooserListener.onAccountChosen(localAccount)); + accountChooserViewHolder.getAccountLayout().setOnClickListener((v) -> moveAccountListener.moveToAccount(localAccount)); Glide .with(context) @@ -86,7 +86,7 @@ public class AccountChooserAdapter extends RecyclerView.Adapter<RecyclerView.Vie } } - public interface AccountChooserListener { - void onAccountChosen(LocalAccount account); + public interface MoveAccountListener { + void moveToAccount(LocalAccount account); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java index 18d56ebb..a2fe34cf 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.graphics.Color; import android.graphics.drawable.Icon; import android.os.Build; import android.os.Bundle; @@ -14,9 +15,11 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -30,19 +33,24 @@ import it.niedermann.owncloud.notes.android.activity.EditNoteActivity; import it.niedermann.owncloud.notes.android.fragment.CategoryDialogFragment.CategoryDialogListener; import it.niedermann.owncloud.notes.android.fragment.EditTitleDialogFragment.EditTitleListener; import it.niedermann.owncloud.notes.model.ApiVersion; +import it.niedermann.owncloud.notes.branding.BrandedFragment; import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.DBStatus; import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.persistence.NotesDatabase; +import it.niedermann.owncloud.notes.util.ColorUtil; import it.niedermann.owncloud.notes.util.NoteUtil; import it.niedermann.owncloud.notes.util.ShareUtil; import static androidx.core.content.pm.ShortcutManagerCompat.isRequestPinShortcutSupported; import static it.niedermann.owncloud.notes.android.activity.EditNoteActivity.ACTION_SHORTCUT; +import static it.niedermann.owncloud.notes.branding.BrandingUtil.tintMenuIcon; +import static it.niedermann.owncloud.notes.util.ColorUtil.isColorDark; +import static it.niedermann.owncloud.notes.util.Notes.isDarkThemeActive; -public abstract class BaseNoteFragment extends Fragment implements CategoryDialogListener, EditTitleListener { +public abstract class BaseNoteFragment extends BrandedFragment implements CategoryDialogListener, EditTitleListener { private static final String TAG = BaseNoteFragment.class.getSimpleName(); @@ -146,12 +154,14 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo } @Override - public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { inflater.inflate(R.menu.menu_note_fragment, menu); if (isRequestPinShortcutSupported(requireActivity()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { menu.add(Menu.NONE, MENU_ID_PIN, 110, R.string.pin_to_homescreen); } + + super.onCreateOptionsMenu(menu, inflater); } @Override @@ -167,6 +177,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo private void prepareFavoriteOption(MenuItem item) { item.setIcon(note.isFavorite() ? R.drawable.ic_star_white_24dp : R.drawable.ic_star_border_white_24dp); item.setChecked(note.isFavorite()); + tintMenuIcon(item, colorAccent); } /** @@ -199,7 +210,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo showEditTitleDialog(); return true; case R.id.menu_move: - AccountChooserDialogFragment.newInstance().show(requireActivity().getSupportFragmentManager(), BaseNoteFragment.class.getSimpleName()); + MoveAccountDialogFragment.newInstance().show(requireActivity().getSupportFragmentManager(), BaseNoteFragment.class.getSimpleName()); return true; case R.id.menu_share: ShareUtil.openShareDialog(requireContext(), note.getTitle(), note.getContent()); @@ -340,6 +351,39 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo listener.close(); } + @ColorInt + protected static int getTextHighlightBackgroundColor(@NonNull Context context, @ColorInt int mainColor, @ColorInt int colorPrimary, @ColorInt int colorAccent) { + if (isDarkThemeActive(context)) { // Dark background + if (isColorDark(mainColor)) { // Dark brand color + if (ColorUtil.contrastRatioIsSufficient(mainColor, colorPrimary)) { // But also dark text + return mainColor; + } else { + return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); + } + } else { // Light brand color + if (ColorUtil.contrastRatioIsSufficient(mainColor, colorAccent)) { // But also dark text + return Color.argb(77, Color.red(mainColor), Color.green(mainColor), Color.blue(mainColor)); + } else { + return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); + } + } + } else { // Light background + if (isColorDark(mainColor)) { // Dark brand color + if (ColorUtil.contrastRatioIsSufficient(mainColor, colorAccent)) { // But also dark text + return Color.argb(77, Color.red(mainColor), Color.green(mainColor), Color.blue(mainColor)); + } else { + return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); + } + } else { // Light brand color + if (ColorUtil.contrastRatioIsSufficient(mainColor, colorPrimary)) { // But also dark text + return mainColor; + } else { + return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); + } + } + } + } + public interface NoteFragmentListener { void close(); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/CategoryDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/CategoryDialogFragment.java index a05ad4b7..cd194cc0 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/CategoryDialogFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/CategoryDialogFragment.java @@ -1,6 +1,5 @@ package it.niedermann.owncloud.notes.android.fragment; -import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; @@ -14,12 +13,14 @@ import android.view.WindowManager; import android.widget.EditText; import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatDialogFragment; import androidx.fragment.app.Fragment; import java.util.List; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandedAlertDialogBuilder; +import it.niedermann.owncloud.notes.branding.BrandedDialogFragment; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.DialogChangeCategoryBinding; import it.niedermann.owncloud.notes.model.NavigationAdapter; import it.niedermann.owncloud.notes.persistence.NotesDatabase; @@ -29,16 +30,22 @@ import it.niedermann.owncloud.notes.persistence.NotesDatabase; * It targetFragment is set it must implement the interface {@link CategoryDialogListener}. * The calling Activity must implement the interface {@link CategoryDialogListener}. */ -public class CategoryDialogFragment extends AppCompatDialogFragment { +public class CategoryDialogFragment extends BrandedDialogFragment { private static final String TAG = CategoryDialogFragment.class.getSimpleName(); private static final String STATE_CATEGORY = "category"; + private DialogChangeCategoryBinding binding; private NotesDatabase db; private CategoryDialogListener listener; private EditText editCategory; + @Override + public void applyBrand(int mainColor, int textColor) { + BrandingUtil.applyBrandToEditText(mainColor, textColor, binding.search); + } + /** * Interface that must be implemented by the calling Activity. */ @@ -81,7 +88,7 @@ public class CategoryDialogFragment extends AppCompatDialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { View dialogView = View.inflate(getContext(), R.layout.dialog_change_category, null); - DialogChangeCategoryBinding binding = DialogChangeCategoryBinding.bind(dialogView); + binding = DialogChangeCategoryBinding.bind(dialogView); this.editCategory = binding.search; if (savedInstanceState == null) { @@ -131,7 +138,7 @@ public class CategoryDialogFragment extends AppCompatDialogFragment { } }); - return new AlertDialog.Builder(getActivity()) + return new BrandedAlertDialogBuilder(getActivity()) .setTitle(R.string.change_category_title) .setView(dialogView) .setCancelable(true) diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/AccountChooserDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/MoveAccountDialogFragment.java index 880eafb6..ec0d2ff5 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/AccountChooserDialogFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/MoveAccountDialogFragment.java @@ -10,7 +10,6 @@ import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatDialogFragment; import androidx.recyclerview.widget.RecyclerView; @@ -18,27 +17,28 @@ import java.util.List; import java.util.Objects; import it.niedermann.owncloud.notes.R; -import it.niedermann.owncloud.notes.android.fragment.AccountChooserAdapter.AccountChooserListener; +import it.niedermann.owncloud.notes.android.fragment.AccountChooserAdapter.MoveAccountListener; +import it.niedermann.owncloud.notes.branding.BrandedAlertDialogBuilder; import it.niedermann.owncloud.notes.databinding.DialogChooseAccountBinding; import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.persistence.NotesDatabase; -public class AccountChooserDialogFragment extends AppCompatDialogFragment implements AccountChooserListener { - private AccountChooserListener accountChooserListener; +public class MoveAccountDialogFragment extends AppCompatDialogFragment implements MoveAccountListener { + private MoveAccountListener moveAccountListener; /** * Use newInstance()-Method */ - public AccountChooserDialogFragment() { + public MoveAccountDialogFragment() { } @Override public void onAttach(@NonNull Context context) { super.onAttach(context); - if (context instanceof AccountChooserListener) { - this.accountChooserListener = (AccountChooserListener) context; + if (context instanceof MoveAccountListener) { + this.moveAccountListener = (MoveAccountListener) context; } else { - throw new ClassCastException("Caller must implement " + AccountChooserListener.class.getSimpleName()); + throw new ClassCastException("Caller must implement " + MoveAccountListener.class.getSimpleName()); } } @@ -54,7 +54,7 @@ public class AccountChooserDialogFragment extends AppCompatDialogFragment implem RecyclerView.Adapter adapter = new AccountChooserAdapter(accountsList, this, requireActivity()); binding.accountsList.setAdapter(adapter); - return new AlertDialog.Builder(requireActivity()) + return new BrandedAlertDialogBuilder(requireActivity()) .setView(binding.getRoot()) .setTitle(R.string.simple_move) .setNegativeButton(android.R.string.cancel, null) @@ -68,13 +68,13 @@ public class AccountChooserDialogFragment extends AppCompatDialogFragment implem return super.onCreateView(inflater, container, savedInstanceState); } - public static AccountChooserDialogFragment newInstance() { - return new AccountChooserDialogFragment(); + public static MoveAccountDialogFragment newInstance() { + return new MoveAccountDialogFragment(); } @Override - public void onAccountChosen(LocalAccount account) { - accountChooserListener.onAccountChosen(account); + public void moveToAccount(LocalAccount account) { + moveAccountListener.moveToAccount(account); dismiss(); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java index 9caa38a8..b1125092 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java @@ -243,10 +243,16 @@ public class NoteEditFragment extends SearchableBaseNoteFragment { } @Override - protected void colorWithText(@NonNull String newText, @Nullable Integer current) { + protected void colorWithText(@NonNull String newText, @Nullable Integer current, int mainColor, int textColor) { if (binding != null && isAttachedToWindow(binding.editContent)) { binding.editContent.clearFocus(); - binding.editContent.setText(searchAndColor(new SpannableString(getContent()), newText, requireContext(), current), TextView.BufferType.SPANNABLE); + binding.editContent.setText(searchAndColor(new SpannableString(getContent()), newText, requireContext(), current, mainColor, textColor), TextView.BufferType.SPANNABLE); } } + + @Override + public void applyBrand(int mainColor, int textColor) { + super.applyBrand(mainColor, textColor); + binding.editContent.setHighlightColor(getTextHighlightBackgroundColor(requireContext(), mainColor, colorPrimary, colorAccent)); + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java index 85fd5d7a..8aa258b8 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java @@ -182,10 +182,10 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O } @Override - protected void colorWithText(@NonNull String newText, @Nullable Integer current) { + protected void colorWithText(@NonNull String newText, @Nullable Integer current, int mainColor, int textColor) { if (binding != null && ViewCompat.isAttachedToWindow(binding.singleNoteContent)) { binding.singleNoteContent.setText( - searchAndColor(new SpannableString(parseCompat(markdownProcessor, getContent())), newText, requireContext(), current), + searchAndColor(new SpannableString(parseCompat(markdownProcessor, getContent())), newText, requireContext(), current, mainColor, textColor), TextView.BufferType.SPANNABLE); } } @@ -216,4 +216,10 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O Toast.makeText(requireContext(), getString(R.string.error_sync, getString(R.string.error_no_network)), Toast.LENGTH_LONG).show(); } } + + @Override + public void applyBrand(int mainColor, int textColor) { + super.applyBrand(mainColor, textColor); + binding.singleNoteContent.setHighlightColor(getTextHighlightBackgroundColor(requireContext(), mainColor, colorPrimary, colorAccent)); + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java index c0c8e2fb..fe8b5273 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java @@ -147,9 +147,9 @@ public class NoteReadonlyFragment extends SearchableBaseNoteFragment { } @Override - protected void colorWithText(@NonNull String newText, @Nullable Integer current) { + protected void colorWithText(@NonNull String newText, @Nullable Integer current, int mainColor, int textColor) { if ((binding != null) && isAttachedToWindow(binding.singleNoteContent)) { - binding.singleNoteContent.setText(searchAndColor(new SpannableString(parseCompat(markdownProcessor, getContent())), newText, requireContext(), current), TextView.BufferType.SPANNABLE); + binding.singleNoteContent.setText(searchAndColor(new SpannableString(parseCompat(markdownProcessor, getContent())), newText, requireContext(), current, mainColor, textColor), TextView.BufferType.SPANNABLE); } } @@ -157,4 +157,10 @@ public class NoteReadonlyFragment extends SearchableBaseNoteFragment { protected String getContent() { return note.getContent(); } + + @Override + public void applyBrand(int mainColor, int textColor) { + super.applyBrand(mainColor, textColor); + binding.singleNoteContent.setHighlightColor(getTextHighlightBackgroundColor(requireContext(), mainColor, colorPrimary, colorAccent)); + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java index 27ce4fc7..ccc046cf 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java @@ -1,24 +1,34 @@ package it.niedermann.owncloud.notes.android.fragment; import android.app.Activity; +import android.content.Context; import android.os.Bundle; import android.util.Log; +import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import androidx.preference.ListPreference; +import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.SwitchPreference; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.DarkModeSetting; +import it.niedermann.owncloud.notes.branding.Branded; +import it.niedermann.owncloud.notes.branding.BrandedSwitchPreference; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.persistence.SyncWorker; import it.niedermann.owncloud.notes.util.DeviceCredentialUtil; import it.niedermann.owncloud.notes.util.Notes; -public class PreferencesFragment extends PreferenceFragmentCompat { +public class PreferencesFragment extends PreferenceFragmentCompat implements Branded { private static final String TAG = PreferencesFragment.class.getSimpleName(); + private BrandedSwitchPreference fontPref; + private BrandedSwitchPreference lockPref; + private BrandedSwitchPreference wifiOnlyPref; + private BrandedSwitchPreference brandingPref; + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -28,10 +38,26 @@ public class PreferencesFragment extends PreferenceFragmentCompat { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.preferences); - final SwitchPreference lockPref = findPreference(getString(R.string.pref_key_lock)); + fontPref = findPreference(getString(R.string.pref_key_font)); + + brandingPref = findPreference(getString(R.string.pref_key_branding)); + if (brandingPref != null) { + brandingPref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> { + final Boolean branding = (Boolean) newValue; + Log.v(TAG, "branding: " + branding); + requireActivity().setResult(Activity.RESULT_OK); + requireActivity().recreate(); + return true; + }); + } else { + Log.e(TAG, "Could not find preference with key: \"" + getString(R.string.pref_key_branding) + "\""); + } + + lockPref = findPreference(getString(R.string.pref_key_lock)); if (lockPref != null) { if (!DeviceCredentialUtil.areCredentialsAvailable(requireContext())) { lockPref.setVisible(false); + findPreference(getString(R.string.pref_category_security)).setVisible(false); } else { lockPref.setOnPreferenceChangeListener((preference, newValue) -> { Notes.setLockedPreference((Boolean) newValue); @@ -51,7 +77,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat { return true; }); - final SwitchPreference wifiOnlyPref = findPreference(getString(R.string.pref_key_wifi_only)); + wifiOnlyPref = findPreference(getString(R.string.pref_key_wifi_only)); assert wifiOnlyPref != null; wifiOnlyPref.setOnPreferenceChangeListener((preference, newValue) -> { Log.i(TAG, "syncOnWifiOnly: " + newValue); @@ -66,4 +92,24 @@ public class PreferencesFragment extends PreferenceFragmentCompat { return true; }); } + + + @Override + public void onStart() { + super.onStart(); + @Nullable Context context = getContext(); + if (context != null) { + @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(context); + @ColorInt final int textColor = BrandingUtil.readBrandTextColor(context); + applyBrand(mainColor, textColor); + } + } + + @Override + public void applyBrand(int mainColor, int textColor) { + fontPref.applyBrand(mainColor, textColor); + lockPref.applyBrand(mainColor, textColor); + wifiOnlyPref.applyBrand(mainColor, textColor); + brandingPref.applyBrand(mainColor, textColor); + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/SearchableBaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/SearchableBaseNoteFragment.java index 10080457..f0949432 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/SearchableBaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/SearchableBaseNoteFragment.java @@ -1,5 +1,6 @@ package it.niedermann.owncloud.notes.android.fragment; +import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.text.Layout; @@ -12,6 +13,8 @@ import android.view.ViewTreeObserver; import android.widget.LinearLayout; import android.widget.ScrollView; +import androidx.annotation.CallSuper; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.SearchView; @@ -22,6 +25,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandedActivity; public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { @@ -35,6 +39,18 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { private String searchQuery = null; private static final int delay = 50; // If the search string does not change after $delay ms, then the search task starts. + @ColorInt + private int mainColor; + @ColorInt + private int textColor; + + @Override + public void onStart() { + this.mainColor = getResources().getColor(R.color.defaultBrand); + this.textColor = Color.WHITE; + super.onStart(); + } + @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -73,12 +89,12 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { if (currentVisibility != oldVisibility) { if (currentVisibility != View.VISIBLE) { - colorWithText("", null); + colorWithText("", null, mainColor, textColor); searchQuery = ""; hideSearchFabs(); } else { jumpToOccurrence(); - colorWithText(searchQuery, null); + colorWithText(searchQuery, null, mainColor, textColor); occurrenceCount = countOccurrences(getContent(), searchQuery); showSearchFabs(); } @@ -96,7 +112,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { next.setOnClickListener(v -> { currentOccurrence++; jumpToOccurrence(); - colorWithText(searchView.getQuery().toString(), currentOccurrence); + colorWithText(searchView.getQuery().toString(), currentOccurrence, mainColor, textColor); }); } @@ -105,7 +121,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { occurrenceCount = countOccurrences(getContent(), searchView.getQuery().toString()); currentOccurrence--; jumpToOccurrence(); - colorWithText(searchView.getQuery().toString(), currentOccurrence); + colorWithText(searchView.getQuery().toString(), currentOccurrence, mainColor, textColor); }); } @@ -117,7 +133,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { public boolean onQueryTextSubmit(@NonNull String query) { currentOccurrence++; jumpToOccurrence(); - colorWithText(query, currentOccurrence); + colorWithText(query, currentOccurrence, mainColor, textColor); return true; } @@ -137,7 +153,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } currentOccurrence = 1; jumpToOccurrence(); - colorWithText(searchQuery, currentOccurrence); + colorWithText(searchQuery, currentOccurrence, mainColor, textColor); } private void queryWithHandler(@NonNull String newText) { @@ -183,7 +199,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } } - protected abstract void colorWithText(@NonNull String newText, @Nullable Integer current); + protected abstract void colorWithText(@NonNull String newText, @Nullable Integer current, int mainColor, int textColor); protected abstract ScrollView getScrollView(); @@ -273,4 +289,13 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } return count; } + + @CallSuper + @Override + public void applyBrand(int mainColor, int textColor) { + this.mainColor = mainColor; + this.textColor = textColor; + BrandedActivity.applyBrandToFAB(mainColor, textColor, getSearchPrevButton()); + BrandedActivity.applyBrandToFAB(mainColor, textColor, getSearchNextButton()); + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentLicenseTab.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentLicenseTab.java index 4ce5d5dc..b5b984cd 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentLicenseTab.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentLicenseTab.java @@ -1,20 +1,27 @@ package it.niedermann.owncloud.notes.android.fragment.about; import android.content.Intent; +import android.content.res.ColorStateList; import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; +import androidx.core.graphics.drawable.DrawableCompat; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandedFragment; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.FragmentAboutLicenseTabBinding; +import it.niedermann.owncloud.notes.util.ColorUtil; import it.niedermann.owncloud.notes.util.SupportUtil; -public class AboutFragmentLicenseTab extends Fragment { +public class AboutFragmentLicenseTab extends BrandedFragment { + + private FragmentAboutLicenseTabBinding binding; private void openLicense() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_license)))); @@ -22,9 +29,16 @@ public class AboutFragmentLicenseTab extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - FragmentAboutLicenseTabBinding binding = FragmentAboutLicenseTabBinding.inflate(inflater, container, false); + binding = FragmentAboutLicenseTabBinding.inflate(inflater, container, false); binding.aboutAppLicenseButton.setOnClickListener((v) -> openLicense()); SupportUtil.setHtml(binding.aboutIconsDisclaimer, R.string.about_icons_disclaimer, getString(R.string.about_app_icon_author)); return binding.getRoot(); } + + @Override + public void applyBrand(int mainColor, int textColor) { + @ColorInt final int finalMainColor = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); + DrawableCompat.setTintList(binding.aboutAppLicenseButton.getBackground(), ColorStateList.valueOf(finalMainColor)); + binding.aboutAppLicenseButton.setTextColor(ColorUtil.getForegroundColorForBackgroundColor(finalMainColor)); + } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/Branded.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/Branded.java new file mode 100644 index 00000000..7ef9138d --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/Branded.java @@ -0,0 +1,9 @@ +package it.niedermann.owncloud.notes.branding; + +import androidx.annotation.ColorInt; +import androidx.annotation.UiThread; + +public interface Branded { + @UiThread + void applyBrand(@ColorInt int mainColor, @ColorInt int textColor); +}
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java new file mode 100644 index 00000000..0eea5bff --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java @@ -0,0 +1,67 @@ +package it.niedermann.owncloud.notes.branding; + +import android.content.res.ColorStateList; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.util.TypedValue; +import android.view.Menu; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; + +import it.niedermann.owncloud.notes.R; + +import static it.niedermann.owncloud.notes.branding.BrandingUtil.tintMenuIcon; + +public abstract class BrandedActivity extends AppCompatActivity implements Branded { + + @ColorInt + protected int colorAccent; + + public static void applyBrandToFAB(@ColorInt int mainColor, @ColorInt int textColor, @NonNull FloatingActionButton fab) { + fab.setSupportBackgroundTintList(ColorStateList.valueOf(mainColor)); + fab.setColorFilter(textColor); + } + + @Override + protected void onStart() { + super.onStart(); + + final TypedValue typedValue = new TypedValue(); + getTheme().resolveAttribute(R.attr.colorAccent, typedValue, true); + colorAccent = typedValue.data; + + if (BrandingUtil.isBrandingEnabled(this)) { + @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(this); + @ColorInt final int textColor = BrandingUtil.readBrandTextColor(this); + applyBrand(mainColor, textColor); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + for (int i = 0; i < menu.size(); i++) { + tintMenuIcon(menu.getItem(i), colorAccent); + } + return super.onCreateOptionsMenu(menu); + } + + public void applyBrandToPrimaryToolbar(@NonNull Toolbar toolbar) { + final Drawable overflowDrawable = toolbar.getOverflowIcon(); + if (overflowDrawable != null) { + overflowDrawable.setColorFilter(colorAccent, PorterDuff.Mode.SRC_ATOP); + toolbar.setOverflowIcon(overflowDrawable); + } + + final Drawable navigationDrawable = toolbar.getNavigationIcon(); + if (navigationDrawable != null) { + navigationDrawable.setColorFilter(colorAccent, PorterDuff.Mode.SRC_ATOP); + toolbar.setNavigationIcon(navigationDrawable); + } + } + +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedAlertDialogBuilder.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedAlertDialogBuilder.java new file mode 100644 index 00000000..940d978d --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedAlertDialogBuilder.java @@ -0,0 +1,48 @@ +package it.niedermann.owncloud.notes.branding; + +import android.content.Context; +import android.content.DialogInterface; +import android.widget.Button; + +import androidx.annotation.CallSuper; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import static it.niedermann.owncloud.notes.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; + +public class BrandedAlertDialogBuilder extends AlertDialog.Builder implements Branded { + + protected AlertDialog dialog; + + public BrandedAlertDialogBuilder(Context context) { + super(context); + } + + @NonNull + @Override + public AlertDialog create() { + this.dialog = super.create(); + + @NonNull Context context = getContext(); + @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(context); + @ColorInt final int textColor = BrandingUtil.readBrandTextColor(context); + applyBrand(mainColor, textColor); + dialog.setOnShowListener(dialog -> applyBrand(mainColor, textColor)); + return dialog; + } + + @CallSuper + @Override + public void applyBrand(int mainColor, int textColor) { + final Button[] buttons = new Button[3]; + buttons[0] = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + buttons[1] = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); + buttons[2] = dialog.getButton(DialogInterface.BUTTON_NEUTRAL); + for (Button button : buttons) { + if (button != null) { + button.setTextColor(getSecondaryForegroundColorDependingOnTheme(button.getContext(), mainColor)); + } + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDeleteAlertDialogBuilder.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDeleteAlertDialogBuilder.java new file mode 100644 index 00000000..113acf49 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDeleteAlertDialogBuilder.java @@ -0,0 +1,26 @@ +package it.niedermann.owncloud.notes.branding; + +import android.content.Context; +import android.content.DialogInterface; +import android.widget.Button; + +import androidx.annotation.CallSuper; + +import it.niedermann.owncloud.notes.R; + +public class BrandedDeleteAlertDialogBuilder extends BrandedAlertDialogBuilder { + + public BrandedDeleteAlertDialogBuilder(Context context) { + super(context); + } + + @CallSuper + @Override + public void applyBrand(int mainColor, int textColor) { + super.applyBrand(mainColor, textColor); + final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (positiveButton != null) { + positiveButton.setTextColor(getContext().getResources().getColor(R.color.bg_attention)); + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDialogFragment.java new file mode 100644 index 00000000..63eb2a69 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDialogFragment.java @@ -0,0 +1,24 @@ +package it.niedermann.owncloud.notes.branding; + +import android.content.Context; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; + +public abstract class BrandedDialogFragment extends DialogFragment implements Branded { + + @Override + public void onStart() { + super.onStart(); + + @Nullable Context context = getContext(); + if (context != null) { + if (BrandingUtil.isBrandingEnabled(context)) { + @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(context); + @ColorInt final int textColor = BrandingUtil.readBrandTextColor(context); + applyBrand(mainColor, textColor); + } + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java new file mode 100644 index 00000000..5752bff8 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java @@ -0,0 +1,49 @@ +package it.niedermann.owncloud.notes.branding; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Menu; +import android.view.MenuInflater; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import it.niedermann.owncloud.notes.R; + +import static it.niedermann.owncloud.notes.branding.BrandingUtil.tintMenuIcon; + +public abstract class BrandedFragment extends Fragment implements Branded { + + @ColorInt + protected int colorAccent; + @ColorInt + protected int colorPrimary; + + @Override + public void onStart() { + super.onStart(); + + final TypedValue typedValue = new TypedValue(); + requireActivity().getTheme().resolveAttribute(R.attr.colorAccent, typedValue, true); + colorAccent = typedValue.data; + requireActivity().getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true); + colorPrimary = typedValue.data; + + @Nullable Context context = getContext(); + if (context != null && BrandingUtil.isBrandingEnabled(context)) { + @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(context); + @ColorInt final int textColor = BrandingUtil.readBrandTextColor(context); + applyBrand(mainColor, textColor); + } + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + for (int i = 0; i < menu.size(); i++) { + tintMenuIcon(menu.getItem(i), colorAccent); + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedPreferenceCategory.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedPreferenceCategory.java new file mode 100644 index 00000000..d2bcd274 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedPreferenceCategory.java @@ -0,0 +1,46 @@ +package it.niedermann.owncloud.notes.branding; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceViewHolder; + +import static it.niedermann.owncloud.notes.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; + +public class BrandedPreferenceCategory extends PreferenceCategory { + + public BrandedPreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public BrandedPreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public BrandedPreferenceCategory(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BrandedPreferenceCategory(Context context) { + super(context); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + if (BrandingUtil.isBrandingEnabled(getContext())) { + final View v = holder.itemView.findViewById(android.R.id.title); + @Nullable final Context context = getContext(); + if (context != null && v instanceof TextView) { + @ColorInt final int mainColor = getSecondaryForegroundColorDependingOnTheme(context, BrandingUtil.readBrandMainColor(context)); + ((TextView) v).setTextColor(mainColor); + } + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSnackbar.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSnackbar.java new file mode 100644 index 00000000..42b9c5cb --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSnackbar.java @@ -0,0 +1,31 @@ +package it.niedermann.owncloud.notes.branding; + +import android.graphics.Color; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import com.google.android.material.snackbar.BaseTransientBottomBar; +import com.google.android.material.snackbar.Snackbar; + +import it.niedermann.owncloud.notes.util.ColorUtil; + +public class BrandedSnackbar { + + @NonNull + public static Snackbar make(@NonNull View view, @NonNull CharSequence text, @BaseTransientBottomBar.Duration int duration) { + final Snackbar snackbar = Snackbar.make(view, text, duration); + if (BrandingUtil.isBrandingEnabled(view.getContext())) { + int color = BrandingUtil.readBrandMainColor(view.getContext()); + snackbar.setActionTextColor(ColorUtil.isColorDark(color) ? Color.WHITE : color); + } + return snackbar; + } + + @NonNull + public static Snackbar make(@NonNull View view, @StringRes int resId, @BaseTransientBottomBar.Duration int duration) { + return make(view, view.getResources().getText(resId), duration); + } + +}
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSwitchPreference.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSwitchPreference.java new file mode 100644 index 00000000..541d2f4a --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSwitchPreference.java @@ -0,0 +1,116 @@ +package it.niedermann.owncloud.notes.branding; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.preference.PreferenceViewHolder; +import androidx.preference.SwitchPreference; + +import it.niedermann.owncloud.notes.R; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.JELLY_BEAN; +import static it.niedermann.owncloud.notes.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; + +public class BrandedSwitchPreference extends SwitchPreference implements Branded { + + @ColorInt + private Integer mainColor = null; + + @ColorInt + private Integer textColor = null; + + @Nullable + private Switch switchView; + + public BrandedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public BrandedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public BrandedSwitchPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BrandedSwitchPreference(Context context) { + super(context); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + if (holder.itemView instanceof ViewGroup) { + switchView = findSwitchWidget(holder.itemView); + if (mainColor != null && textColor != null) { + applyBrand(); + } + } + } + + @Override + public void applyBrand(@ColorInt int mainColor, @ColorInt int textColor) { + if (BrandingUtil.isBrandingEnabled(getContext())) { + this.mainColor = mainColor; + this.textColor = textColor; + } else { + this.mainColor = getContext().getResources().getColor(R.color.defaultBrand); + this.textColor = Color.WHITE; + } + // onBindViewHolder is called after applyBrand, therefore we have to store the given values and apply them later. + applyBrand(); + } + + private void applyBrand() { + if (switchView != null && SDK_INT >= JELLY_BEAN) { + final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(getContext(), mainColor); + // int trackColor = Color.argb(77, Color.red(finalMainColor), Color.green(finalMainColor), Color.blue(finalMainColor)); + DrawableCompat.setTintList(switchView.getThumbDrawable(), new ColorStateList( + new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}}, + new int[]{finalMainColor, getContext().getResources().getColor(R.color.fg_default_low)} + )); + DrawableCompat.setTintList(switchView.getTrackDrawable(), new ColorStateList( + new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}}, + new int[]{finalMainColor, getContext().getResources().getColor(R.color.fg_default_low)} + )); + } + } + + /** + * Recursively go through view tree until we find an android.widget.Switch + * + * @param view Root view to start searching + * @return A Switch class or null + * @see <a href="https://gist.github.com/marchold/45e22839eb94aa14dfb5">Source</a> + */ + private Switch findSwitchWidget(View view) { + if (view instanceof Switch) { + return (Switch) view; + } + if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View child = viewGroup.getChildAt(i); + if (child instanceof ViewGroup) { + Switch result = findSwitchWidget(child); + if (result != null) return result; + } + if (child instanceof Switch) { + return (Switch) child; + } + } + } + return null; + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java new file mode 100644 index 00000000..f84bd8e4 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java @@ -0,0 +1,134 @@ +package it.niedermann.owncloud.notes.branding; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.util.Log; +import android.view.MenuItem; +import android.widget.EditText; + +import androidx.annotation.ColorInt; +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.preference.PreferenceManager; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.util.Notes; + +import static it.niedermann.owncloud.notes.util.ColorUtil.contrastRatioIsSufficient; + +public class BrandingUtil { + + private static final String TAG = BrandingUtil.class.getSimpleName(); + private static final String pref_key_branding_main = "branding_main"; + private static final String pref_key_branding_text = "branding_text"; + + private BrandingUtil() { + + } + + + public static boolean isBrandingEnabled(@NonNull Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean(context.getString(R.string.pref_key_branding), true); + } + + @ColorInt + public static int readBrandMainColor(@NonNull Context context) { + if (BrandingUtil.isBrandingEnabled(context)) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); + Log.v(TAG, "--- Read: shared_preference_theme_main"); + return sharedPreferences.getInt(pref_key_branding_main, context.getApplicationContext().getResources().getColor(R.color.defaultBrand)); + } else { + return context.getResources().getColor(R.color.defaultBrand); + } + } + + @ColorInt + public static int readBrandTextColor(@NonNull Context context) { + if (isBrandingEnabled(context)) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); + Log.v(TAG, "--- Read: shared_preference_theme_text"); + return sharedPreferences.getInt(pref_key_branding_text, Color.WHITE); + } else { + return Color.WHITE; + } + } + + public static void saveBrandColors(@NonNull Context context, @ColorInt int mainColor, @ColorInt int textColor) { + final int previousMainColor = readBrandMainColor(context); + final int previousTextColor = readBrandTextColor(context); + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); + Log.v(TAG, "--- Write: shared_preference_theme_main" + " | " + mainColor); + Log.v(TAG, "--- Write: shared_preference_theme_text" + " | " + textColor); + editor.putInt(pref_key_branding_main, mainColor); + editor.putInt(pref_key_branding_text, textColor); + editor.apply(); + if (isBrandingEnabled(context) && context instanceof BrandedActivity) { + if (mainColor != previousMainColor || textColor != previousTextColor) { + final BrandedActivity activity = (BrandedActivity) context; + activity.runOnUiThread(activity::recreate); + } + } + } + + /** + * Since we may collide with dark theme in this area, we have to make sure that the color is visible depending on the background + */ + @ColorInt + public static int getSecondaryForegroundColorDependingOnTheme(@NonNull Context context, @ColorInt int mainColor) { + final int primaryColor = context.getResources().getColor(R.color.primary); + final boolean isDarkTheme = Notes.isDarkThemeActive(context); + if (isDarkTheme && !contrastRatioIsSufficient(mainColor, primaryColor)) { + Log.v(TAG, "Contrast ratio between brand color " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and dark theme is too low. Falling back to WHITE as brand color."); + return Color.WHITE; + } else if (!isDarkTheme && !contrastRatioIsSufficient(mainColor, primaryColor)) { + Log.v(TAG, "Contrast ratio between brand color " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and light theme is too low. Falling back to BLACK as brand color."); + return Color.BLACK; + } else { + return mainColor; + } + } + + public static void applyBrandToEditText(@ColorInt int mainColor, @ColorInt int textColor, @NonNull EditText editText) { + @ColorInt final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(editText.getContext(), mainColor); + DrawableCompat.setTintList(editText.getBackground(), new ColorStateList( + new int[][]{ + new int[]{android.R.attr.state_active}, + new int[]{android.R.attr.state_activated}, + new int[]{android.R.attr.state_focused}, + new int[]{android.R.attr.state_pressed}, + new int[]{} + }, + new int[]{ + finalMainColor, + finalMainColor, + finalMainColor, + finalMainColor, + editText.getContext().getResources().getColor(R.color.fg_default_low) + } + )); + } + + public static void tintMenuIcon(@NonNull MenuItem menuItem, @ColorInt int color) { + Drawable drawable = menuItem.getIcon(); + if (drawable != null) { + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable, color); + menuItem.setIcon(drawable); + } + } + + public static void applyBrandToLayerDrawable(@NonNull LayerDrawable check, @IdRes int areaToColor, @ColorInt int mainColor) { + final Drawable drawable = check.findDrawableByLayerId(areaToColor); + if (drawable == null) { + Log.e(TAG, "Could not find areaToColor (" + areaToColor + "). Cannot apply brand."); + } else { + DrawableCompat.setTint(drawable, mainColor); + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountAdapter.java new file mode 100644 index 00000000..50ab925b --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountAdapter.java @@ -0,0 +1,80 @@ +package it.niedermann.owncloud.notes.manageaccounts; + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Consumer; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.model.LocalAccount; + +public class ManageAccountAdapter extends RecyclerView.Adapter<ManageAccountViewHolder> { + + @Nullable + private LocalAccount currentLocalAccount = null; + @NonNull + private final List<LocalAccount> localAccounts = new ArrayList<>(); + @NonNull + private final Consumer<LocalAccount> onAccountClick; + @Nullable + private final Consumer<LocalAccount> onAccountDelete; + + public ManageAccountAdapter(@NonNull Consumer<LocalAccount> onAccountClick, @Nullable Consumer<LocalAccount> onAccountDelete) { + this.onAccountClick = onAccountClick; + this.onAccountDelete = onAccountDelete; + setHasStableIds(true); + } + + @Override + public long getItemId(int position) { + return localAccounts.get(position).getId(); + } + + @NonNull + @Override + public ManageAccountViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ManageAccountViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_account_choose, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ManageAccountViewHolder holder, int position) { + final LocalAccount localAccount = localAccounts.get(position); + holder.bind(localAccount, (localAccountClicked) -> { + setCurrentLocalAccount(localAccountClicked); + onAccountClick.accept(localAccountClicked); + }, (localAccountToDelete -> { + if (onAccountDelete != null) { + for (int i = 0; i < localAccounts.size(); i++) { + if (localAccounts.get(i).getId() == localAccountToDelete.getId()) { + localAccounts.remove(i); + notifyItemRemoved(i); + break; + } + } + onAccountDelete.accept(localAccountToDelete); + } + }), currentLocalAccount != null && currentLocalAccount.getId() == localAccount.getId()); + } + + @Override + public int getItemCount() { + return localAccounts.size(); + } + + public void setLocalAccounts(@NonNull List<LocalAccount> localAccounts) { + this.localAccounts.clear(); + this.localAccounts.addAll(localAccounts); + notifyDataSetChanged(); + } + + public void setCurrentLocalAccount(@Nullable LocalAccount currentLocalAccount) { + this.currentLocalAccount = currentLocalAccount; + notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java new file mode 100644 index 00000000..df56fba5 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java @@ -0,0 +1,53 @@ +package it.niedermann.owncloud.notes.manageaccounts; + +import android.graphics.drawable.LayerDrawable; +import android.net.Uri; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Consumer; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.databinding.ItemAccountChooseBinding; +import it.niedermann.owncloud.notes.model.LocalAccount; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static it.niedermann.owncloud.notes.branding.BrandingUtil.applyBrandToLayerDrawable; + +public class ManageAccountViewHolder extends RecyclerView.ViewHolder { + + private ItemAccountChooseBinding binding; + + public ManageAccountViewHolder(@NonNull View itemView) { + super(itemView); + binding = ItemAccountChooseBinding.bind(itemView); + } + + public void bind(@NonNull LocalAccount localAccount, @NonNull Consumer<LocalAccount> onAccountClick, @Nullable Consumer<LocalAccount> onAccountDelete, boolean isCurrentAccount) { + binding.accountItemLabel.setText(localAccount.getAccountName()); + Glide.with(itemView.getContext()) + .load(localAccount.getUrl() + "/index.php/avatar/" + Uri.encode(localAccount.getUserName()) + "/64") + .error(R.drawable.ic_account_circle_grey_24dp) + .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)); + } + if (isCurrentAccount) { + binding.currentAccountIndicator.setVisibility(VISIBLE); + applyBrandToLayerDrawable((LayerDrawable) binding.currentAccountIndicator.getDrawable(), R.id.area, localAccount.getColor()); + } else { + binding.currentAccountIndicator.setVisibility(GONE); + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java new file mode 100644 index 00000000..123f345e --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java @@ -0,0 +1,74 @@ +package it.niedermann.owncloud.notes.manageaccounts; + +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; +import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; +import com.nextcloud.android.sso.helper.SingleAccountHelper; +import com.nextcloud.android.sso.model.SingleSignOnAccount; + +import java.util.List; + +import it.niedermann.owncloud.notes.android.activity.LockedActivity; +import it.niedermann.owncloud.notes.databinding.ActivityManageAccountsBinding; +import it.niedermann.owncloud.notes.model.LocalAccount; +import it.niedermann.owncloud.notes.persistence.NotesDatabase; + +public class ManageAccountsActivity extends LockedActivity { + + private ActivityManageAccountsBinding binding; + private ManageAccountAdapter adapter; + private NotesDatabase db = null; + + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivityManageAccountsBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + setSupportActionBar(binding.toolbar); + + db = NotesDatabase.getInstance(this); + + List<LocalAccount> localAccounts = db.getAccounts(); + + adapter = new ManageAccountAdapter((localAccount) -> { + SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccount.getAccountName()); + }, (localAccount) -> { + db.deleteAccount(localAccount); + for (LocalAccount temp : localAccounts) { + if (temp.getId() == localAccount.getId()) { + localAccounts.remove(temp); + break; + } + } + if (localAccounts.size() > 0) { + SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccounts.get(0).getAccountName()); + adapter.setCurrentLocalAccount(localAccounts.get(0)); + } else { + setResult(AppCompatActivity.RESULT_FIRST_USER); + finish(); + } + }); + adapter.setLocalAccounts(localAccounts); + try { + SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(this); + if (ssoAccount != null) { + adapter.setCurrentLocalAccount(db.getLocalAccountByAccountName(ssoAccount.name)); + } + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); + } + binding.accounts.setAdapter(adapter); + } + + @Override + public void applyBrand(int mainColor, int textColor) { + applyBrandToPrimaryToolbar(binding.toolbar); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/model/ItemAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/model/ItemAdapter.java index c2f2cc4b..ba6042bc 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/model/ItemAdapter.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/model/ItemAdapter.java @@ -1,13 +1,13 @@ package it.niedermann.owncloud.notes.model; -import android.text.Html; +import android.content.Context; +import android.graphics.Color; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; @@ -15,26 +15,31 @@ import java.util.ArrayList; import java.util.List; import it.niedermann.owncloud.notes.R; -import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemBinding; -import it.niedermann.owncloud.notes.databinding.ItemNotesListSectionItemBinding; +import it.niedermann.owncloud.notes.branding.Branded; -import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; - -public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { +public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Branded { private static final String TAG = ItemAdapter.class.getSimpleName(); private static final int section_type = 0; private static final int note_type = 1; private final NoteClickListener noteClickListener; - private List<Item> itemList; + private List<Item> itemList = new ArrayList<>(); private boolean showCategory = true; - private final List<Integer> selected; + private CharSequence searchQuery; + @NonNull + private Context context; + private final List<Integer> selected = new ArrayList<>(); + @ColorInt + private int mainColor; + @ColorInt + private int textColor; - public ItemAdapter(@NonNull NoteClickListener noteClickListener) { - this.itemList = new ArrayList<>(); - this.selected = new ArrayList<>(); - this.noteClickListener = noteClickListener; + public <T extends Context & NoteClickListener> ItemAdapter(@NonNull T context) { + this.context = context; + this.noteClickListener = context; + this.mainColor = context.getResources().getColor(R.color.defaultBrand); + this.textColor = Color.WHITE; } /** @@ -66,7 +71,6 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { notifyDataSetChanged(); } - // Create new views (invoked by the layout manager) @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -77,30 +81,17 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { } else { v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_notes_list_note_item, parent, false); - return new NoteViewHolder(v); + return new NoteViewHolder(v, noteClickListener); } } - // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) { - // - get element from your dataset at this position - // - replace the contents of the view with that element - Item item = itemList.get(position); + final Item item = itemList.get(position); if (item.isSection()) { - SectionItem section = (SectionItem) item; - ((SectionViewHolder) holder).sectionTitle.setText(section.geTitle()); + ((SectionViewHolder) holder).bind((SectionItem) item); } else { - final DBNote note = (DBNote) item; - final NoteViewHolder nvHolder = ((NoteViewHolder) holder); - nvHolder.noteSwipeable.setAlpha(DBStatus.LOCAL_DELETED.equals(note.getStatus()) ? 0.5f : 1.0f); - nvHolder.noteTitle.setText(Html.fromHtml(note.getTitle())); - nvHolder.noteCategory.setVisibility(showCategory && !note.getCategory().isEmpty() ? View.VISIBLE : View.GONE); - nvHolder.noteCategory.setText(Html.fromHtml(note.getCategory())); - nvHolder.noteExcerpt.setText(Html.fromHtml(note.getExcerpt())); - nvHolder.noteStatus.setVisibility(DBStatus.VOID.equals(note.getStatus()) ? View.INVISIBLE : View.VISIBLE); - nvHolder.noteFavorite.setImageResource(note.isFavorite() ? R.drawable.ic_star_yellow_24dp : R.drawable.ic_star_grey_ccc_24dp); - nvHolder.noteFavorite.setOnClickListener(view -> noteClickListener.onNoteFavoriteClick(holder.getAdapterPosition(), view)); + ((NoteViewHolder) holder).bind((DBNote) item, noteClickListener, showCategory, mainColor, textColor, searchQuery); } } @@ -159,63 +150,14 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { return getItem(position).isSection() ? section_type : note_type; } - public interface NoteClickListener { - void onNoteClick(int position, View v); - - void onNoteFavoriteClick(int position, View v); - - boolean onNoteLongClick(int position, View v); - } - - public class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener { - public View noteSwipeable; - private final ItemNotesListNoteItemBinding b; - private final TextView noteTitle; - private final TextView noteCategory; - private final TextView noteExcerpt; - private final ImageView noteStatus; - private final ImageView noteFavorite; - - private NoteViewHolder(View v) { - super(v); - b = ItemNotesListNoteItemBinding.bind(v); - this.noteSwipeable = b.noteSwipeable; - this.noteTitle = b.noteTitle; - this.noteCategory = b.noteCategory; - this.noteExcerpt = b.noteExcerpt; - this.noteStatus = b.noteStatus; - this.noteFavorite = b.noteFavorite; - v.setOnClickListener(this); - v.setOnLongClickListener(this); - } - - @Override - public void onClick(View v) { - final int adapterPosition = getAdapterPosition(); - if (adapterPosition != NO_POSITION) { - noteClickListener.onNoteClick(adapterPosition, v); - } - } - - @Override - public boolean onLongClick(View v) { - return noteClickListener.onNoteLongClick(getAdapterPosition(), v); - } - - public void showSwipe(boolean left) { - b.noteFavoriteLeft.setVisibility(left ? View.VISIBLE : View.INVISIBLE); - b.noteDeleteRight.setVisibility(left ? View.INVISIBLE : View.VISIBLE); - b.noteSwipeFrame.setBackgroundResource(left ? R.color.bg_warning : R.color.bg_attention); - } + @Override + public void applyBrand(int mainColor, int textColor) { + this.mainColor = mainColor; + this.textColor = textColor; + notifyDataSetChanged(); } - public static class SectionViewHolder extends RecyclerView.ViewHolder { - private TextView sectionTitle; - - private SectionViewHolder(View view) { - super(view); - ItemNotesListSectionItemBinding binding = ItemNotesListSectionItemBinding.bind(view); - this.sectionTitle = binding.sectionTitle; - } + public void setHighlightSearchQuery(CharSequence searchQuery) { + this.searchQuery = searchQuery; } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/model/NavigationAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/model/NavigationAdapter.java index f4a4bb36..751fc581 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/model/NavigationAdapter.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/model/NavigationAdapter.java @@ -1,5 +1,6 @@ package it.niedermann.owncloud.notes.model; +import android.content.Context; import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; @@ -7,6 +8,7 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -17,11 +19,16 @@ import java.util.ArrayList; import java.util.List; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.ItemNavigationBinding; import it.niedermann.owncloud.notes.util.NoteUtil; public class NavigationAdapter extends RecyclerView.Adapter<NavigationAdapter.ViewHolder> { + @NonNull + private final Context context; + @ColorInt + private int mainColor; @DrawableRes public static final int ICON_FOLDER = R.drawable.ic_folder_grey600_24dp; @DrawableRes @@ -35,6 +42,11 @@ public class NavigationAdapter extends RecyclerView.Adapter<NavigationAdapter.Vi @DrawableRes public static final int ICON_SUB_MULTIPLE = R.drawable.ic_create_new_folder_grey600_18dp; + public void applyBrand(int mainColor, int textColor) { + this.mainColor = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(context, mainColor); + notifyDataSetChanged(); + } + public static class NavigationItem { @NonNull public String id; @@ -87,7 +99,7 @@ public class NavigationAdapter extends RecyclerView.Adapter<NavigationAdapter.Vi itemView.setOnClickListener(view -> clickListener.onItemClick(currentItem)); } - private void assignItem(@NonNull NavigationItem item) { + private void bind(@NonNull NavigationItem item) { currentItem = item; boolean isSelected = item.id.equals(selectedItem); name.setText(NoteUtil.extendCategory(item.label)); @@ -100,7 +112,7 @@ public class NavigationAdapter extends RecyclerView.Adapter<NavigationAdapter.Vi icon.setVisibility(View.GONE); } view.setBackgroundColor(isSelected ? view.getResources().getColor(R.color.bg_highlighted) : Color.TRANSPARENT); - int textColor = view.getResources().getColor(isSelected ? R.color.primary_dark : R.color.fg_default); + int textColor = isSelected ? mainColor : view.getResources().getColor(R.color.fg_default); name.setTextColor(textColor); count.setTextColor(textColor); @@ -120,7 +132,9 @@ public class NavigationAdapter extends RecyclerView.Adapter<NavigationAdapter.Vi @NonNull private final ClickListener clickListener; - public NavigationAdapter(@NonNull ClickListener clickListener) { + public NavigationAdapter(@NonNull Context context, @NonNull ClickListener clickListener) { + this.context = context; + this.mainColor = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(context, BrandingUtil.readBrandMainColor(context)); this.clickListener = clickListener; } @@ -133,7 +147,7 @@ public class NavigationAdapter extends RecyclerView.Adapter<NavigationAdapter.Vi @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - holder.assignItem(items.get(position)); + holder.bind(items.get(position)); } @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/model/NoteClickListener.java b/app/src/main/java/it/niedermann/owncloud/notes/model/NoteClickListener.java new file mode 100644 index 00000000..8dd1748d --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/model/NoteClickListener.java @@ -0,0 +1,11 @@ +package it.niedermann.owncloud.notes.model; + +import android.view.View; + +public interface NoteClickListener { + void onNoteClick(int position, View v); + + void onNoteFavoriteClick(int position, View v); + + boolean onNoteLongClick(int position, View v); +}
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolder.java new file mode 100644 index 00000000..a42c8377 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolder.java @@ -0,0 +1,143 @@ +package it.niedermann.owncloud.notes.model; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.BackgroundColorSpan; +import android.text.style.ForegroundColorSpan; +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; +import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemBinding; +import it.niedermann.owncloud.notes.util.Notes; + +import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; +import static it.niedermann.owncloud.notes.util.ColorUtil.contrastRatioIsSufficient; +import static it.niedermann.owncloud.notes.util.ColorUtil.isColorDark; + +public class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener { + private final ItemNotesListNoteItemBinding binding; + private final NoteClickListener noteClickListener; + + public NoteViewHolder(View v, NoteClickListener noteClickListener) { + super(v); + binding = ItemNotesListNoteItemBinding.bind(v); + this.noteClickListener = noteClickListener; + v.setOnClickListener(this); + v.setOnLongClickListener(this); + } + + @Override + public void onClick(View v) { + final int adapterPosition = getAdapterPosition(); + if (adapterPosition != NO_POSITION) { + noteClickListener.onNoteClick(adapterPosition, v); + } + } + + @Override + public boolean onLongClick(View v) { + return noteClickListener.onNoteLongClick(getAdapterPosition(), v); + } + + public void showSwipe(boolean left) { + binding.noteFavoriteLeft.setVisibility(left ? View.VISIBLE : View.INVISIBLE); + binding.noteDeleteRight.setVisibility(left ? View.INVISIBLE : View.VISIBLE); + binding.noteSwipeFrame.setBackgroundResource(left ? R.color.bg_warning : R.color.bg_attention); + } + + public void bind(DBNote note, NoteClickListener noteClickListener, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { + @NonNull final Context context = itemView.getContext(); + final boolean isDarkThemeActive = Notes.isDarkThemeActive(context); + + binding.noteSwipeable.setAlpha(DBStatus.LOCAL_DELETED.equals(note.getStatus()) ? 0.5f : 1.0f); + + binding.noteCategory.setVisibility(showCategory && !note.getCategory().isEmpty() ? View.VISIBLE : View.GONE); + binding.noteCategory.setText(note.getCategory()); + + @ColorInt int categoryForeground; + @ColorInt int categoryBackground; + + if (isDarkThemeActive) { + if (isColorDark(mainColor)) { + if (contrastRatioIsSufficient(mainColor, Color.BLACK)) { + categoryBackground = mainColor; + categoryForeground = Color.WHITE; + } else { + categoryBackground = Color.WHITE; + categoryForeground = mainColor; + } + } else { + categoryBackground = mainColor; + categoryForeground = Color.BLACK; + } + } else { + categoryForeground = Color.BLACK; + if (isColorDark(mainColor) || contrastRatioIsSufficient(mainColor, Color.WHITE)) { + categoryBackground = mainColor; + } else { + categoryBackground = Color.BLACK; + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + DrawableCompat.setTint(binding.noteCategory.getBackground(), categoryBackground); + } else { + final GradientDrawable drawable = (GradientDrawable) binding.noteCategory.getBackground(); + drawable.setStroke(1, categoryBackground); + drawable.setColor(isDarkThemeActive ? categoryBackground : Color.TRANSPARENT); + } + binding.noteCategory.setTextColor(categoryForeground); + + binding.noteStatus.setVisibility(DBStatus.VOID.equals(note.getStatus()) ? View.INVISIBLE : View.VISIBLE); + binding.noteFavorite.setImageResource(note.isFavorite() ? R.drawable.ic_star_yellow_24dp : R.drawable.ic_star_grey_ccc_24dp); + binding.noteFavorite.setOnClickListener(view -> noteClickListener.onNoteFavoriteClick(getAdapterPosition(), view)); + + if (!TextUtils.isEmpty(searchQuery)) { + @ColorInt final int searchBackground = context.getResources().getColor(R.color.bg_highlighted); + @ColorInt final int searchForeground = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(context, mainColor); + + // The Pattern.quote method will add \Q to the very beginning of the string and \E to the end of the string + // It implies that the string between \Q and \E is a literal string and thus the reserved keyword in such string will be ignored. + // See https://stackoverflow.com/questions/15409296/what-is-the-use-of-pattern-quote-method + final Pattern pattern = Pattern.compile("(" + Pattern.quote(searchQuery.toString()) + ")", Pattern.CASE_INSENSITIVE); + SpannableString spannableString = new SpannableString(note.getTitle()); + Matcher matcher = pattern.matcher(spannableString); + while (matcher.find()) { + spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0); + spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0); + } + + binding.noteTitle.setText(spannableString); + + spannableString = new SpannableString(note.getExcerpt()); + matcher = pattern.matcher(spannableString); + while (matcher.find()) { + spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0); + spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0); + } + + binding.noteExcerpt.setText(spannableString); + } else { + binding.noteTitle.setText(note.getTitle()); + binding.noteExcerpt.setText(note.getExcerpt()); + } + } + + public View getNoteSwipeable() { + return binding.noteSwipeable; + } +}
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/model/SectionItem.java b/app/src/main/java/it/niedermann/owncloud/notes/model/SectionItem.java index c144211b..27e4758a 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/model/SectionItem.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/model/SectionItem.java @@ -8,7 +8,7 @@ public class SectionItem implements Item { this.title = title; } - public String geTitle() { + public String getTitle() { return title; } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/model/SectionViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/model/SectionViewHolder.java new file mode 100644 index 00000000..3441cc85 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/model/SectionViewHolder.java @@ -0,0 +1,20 @@ +package it.niedermann.owncloud.notes.model; + +import android.view.View; + +import androidx.recyclerview.widget.RecyclerView; + +import it.niedermann.owncloud.notes.databinding.ItemNotesListSectionItemBinding; + +public class SectionViewHolder extends RecyclerView.ViewHolder { + private final ItemNotesListSectionItemBinding binding; + + public SectionViewHolder(View view) { + super(view); + binding = ItemNotesListSectionItemBinding.bind(view); + } + + public void bind(SectionItem item) { + binding.sectionTitle.setText(item.getTitle()); + } +}
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/AbstractNotesDatabase.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/AbstractNotesDatabase.java index 45d58a50..13a37b93 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/AbstractNotesDatabase.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/AbstractNotesDatabase.java @@ -34,7 +34,7 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper { private static final int database_version = 16; @NonNull - private final Context context; + protected final Context context; protected static final String database_name = "OWNCLOUD_NOTES"; protected static final String table_notes = "NOTES"; diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java index a6b77024..4dbdbcbf 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java @@ -1,14 +1,8 @@ package it.niedermann.owncloud.notes.persistence; import android.content.Context; -import android.graphics.Color; import android.os.AsyncTask; -import android.text.Html; -import android.text.SpannableString; -import android.text.TextUtils; import android.text.format.DateUtils; -import android.text.style.BackgroundColorSpan; -import android.text.style.ForegroundColorSpan; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -17,15 +11,12 @@ import androidx.annotation.WorkerThread; import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.model.Category; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.Item; import it.niedermann.owncloud.notes.model.SectionItem; -import it.niedermann.owncloud.notes.util.DisplayUtils; import it.niedermann.owncloud.notes.util.NoteUtil; public class LoadNotesListTask extends AsyncTask<Void, Void, List<Item>> { @@ -35,8 +26,6 @@ public class LoadNotesListTask extends AsyncTask<Void, Void, List<Item>> { private final Category category; private final CharSequence searchQuery; private final long accountId; - private final int searchForeground; - private final int searchBackground; public LoadNotesListTask(long accountId, @NonNull Context context, @NonNull NotesLoadedListener callback, @NonNull Category category, @Nullable CharSequence searchQuery) { this.context = context; @@ -44,8 +33,6 @@ public class LoadNotesListTask extends AsyncTask<Void, Void, List<Item>> { this.category = category; this.searchQuery = searchQuery; this.accountId = accountId; - this.searchBackground = context.getResources().getColor(R.color.bg_highlighted); - this.searchForeground = DisplayUtils.getForeground(Integer.toHexString(this.searchBackground)) ? Color.WHITE : context.getResources().getColor(R.color.primary); } @Override @@ -61,30 +48,6 @@ public class LoadNotesListTask extends AsyncTask<Void, Void, List<Item>> { } } - private DBNote colorTheNote(DBNote dbNote) { - if (!TextUtils.isEmpty(searchQuery)) { - SpannableString spannableString = new SpannableString(dbNote.getTitle()); - Matcher matcher = Pattern.compile("(" + searchQuery + ")", Pattern.CASE_INSENSITIVE).matcher(spannableString); - while (matcher.find()) { - spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0); - spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0); - } - - dbNote.setTitle(Html.toHtml(spannableString)); - - spannableString = new SpannableString(dbNote.getExcerpt()); - matcher = Pattern.compile("(" + searchQuery + ")", Pattern.CASE_INSENSITIVE).matcher(spannableString); - while (matcher.find()) { - spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0); - spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0); - } - - dbNote.setExcerpt(Html.toHtml(spannableString)); - } - - return dbNote; - } - @NonNull @WorkerThread private List<Item> fillListByCategory(@NonNull List<DBNote> noteList) { @@ -95,7 +58,7 @@ public class LoadNotesListTask extends AsyncTask<Void, Void, List<Item>> { itemList.add(new SectionItem(NoteUtil.extendCategory(note.getCategory()))); } - itemList.add(colorTheNote(note)); + itemList.add(note); currentCategory = note.getCategory(); } return itemList; @@ -113,7 +76,7 @@ public class LoadNotesListTask extends AsyncTask<Void, Void, List<Item>> { if (i > 0 && !timeslot.equals(lastTimeslot)) { itemList.add(new SectionItem(timeslot)); } - itemList.add(colorTheNote(currentNote)); + itemList.add(currentNote); lastTimeslot = timeslot; } @@ -122,11 +85,11 @@ public class LoadNotesListTask extends AsyncTask<Void, Void, List<Item>> { @Override protected void onPostExecute(List<Item> items) { - callback.onNotesLoaded(items, category.category == null); + callback.onNotesLoaded(items, category.category == null, searchQuery); } public interface NotesLoadedListener { - void onNotesLoaded(List<Item> notes, boolean showCategory); + void onNotesLoaded(List<Item> notes, boolean showCategory, CharSequence searchQuery); } private class Timeslotter { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java index d48b7529..cb6c2717 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java @@ -34,6 +34,7 @@ import java.util.Set; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.fragment.ExceptionDialogFragment; +import it.niedermann.owncloud.notes.branding.BrandedSnackbar; import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.DBStatus; @@ -499,7 +500,7 @@ public class NoteServerSyncHelper { } if (!status.pullSuccessful || !status.pushSuccessful) { if (context instanceof ViewProvider && context instanceof AppCompatActivity) { - Snackbar.make(((ViewProvider) context).getView(), R.string.error_synchronization, Snackbar.LENGTH_LONG) + BrandedSnackbar.make(((ViewProvider) context).getView(), R.string.error_synchronization, Snackbar.LENGTH_LONG) .setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(exceptions) .show(((AppCompatActivity) context).getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName())) .show(); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java index 63391fc5..fe3600e1 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java @@ -20,6 +20,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import androidx.core.content.ContextCompat; import com.nextcloud.android.sso.AccountImporter; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; @@ -52,6 +53,7 @@ import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.model.NavigationAdapter; import it.niedermann.owncloud.notes.model.NoteListsWidgetData; import it.niedermann.owncloud.notes.model.SingleNoteWidgetData; +import it.niedermann.owncloud.notes.util.ColorUtil; import it.niedermann.owncloud.notes.util.NoteUtil; import static it.niedermann.owncloud.notes.android.activity.EditNoteActivity.ACTION_SHORTCUT; @@ -716,10 +718,9 @@ public class NotesDatabase extends AbstractNotesDatabase { values.put(key_url, url); values.put(key_username, username); values.put(key_account_name, accountName); - values.put(key_color, capabilities.getColor().substring(1)); - values.put(key_text_color, capabilities.getTextColor().substring(1)); values.put(key_capabilities_etag, capabilities.getETag()); - db.insertOrThrow(table_accounts, null, values); + long accountId = db.insertOrThrow(table_accounts, null, values); + updateBrand(accountId, capabilities); } /** @@ -807,15 +808,26 @@ public class NotesDatabase extends AbstractNotesDatabase { public void updateBrand(long accountId, @NonNull Capabilities capabilities) throws IllegalArgumentException { validateAccountId(accountId); - // Validate color format - Color.parseColor(capabilities.getColor()); - Color.parseColor(capabilities.getTextColor()); + + String color; + try { + color = ColorUtil.formatColorToParsableHexString(capabilities.getColor()).substring(1); + } catch (Exception e) { + color = String.format("%06X", (0xFFFFFF & ContextCompat.getColor(context, R.color.defaultBrand))); + } + + String textColor; + try { + textColor = ColorUtil.formatColorToParsableHexString(capabilities.getTextColor()).substring(1); + } catch (Exception e) { + textColor = String.format("%06X", (0xFFFFFF & ContextCompat.getColor(context, android.R.color.white))); + } final SQLiteDatabase db = this.getWritableDatabase(); final ContentValues values = new ContentValues(); - values.put(key_color, capabilities.getColor().substring(1)); - values.put(key_text_color, capabilities.getTextColor().substring(1)); + values.put(key_color, color); + values.put(key_text_color, textColor); final int updatedRows = db.update(table_accounts, values, key_id + " = ?", new String[]{String.valueOf(accountId)}); if (updatedRows == 1) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/util/ColorUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/util/ColorUtil.java index 8649d7a0..3b44c0f2 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/util/ColorUtil.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/util/ColorUtil.java @@ -3,6 +3,7 @@ package it.niedermann.owncloud.notes.util; import android.graphics.Color; import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.util.Pair; @@ -107,4 +108,47 @@ public final class ColorUtil { return result; } } + + /** + * @return well formatted string starting with a hash followed by 6 hex numbers that is parsable by {@link Color#parseColor(String)}. + */ + public static String formatColorToParsableHexString(String input) { + if (input == null) { + throw new IllegalArgumentException("input color string is null"); + } + if (isParsableValidHexColorString(input)) { + return input; + } + final char[] chars = input.replaceAll("#", "").toCharArray(); + final StringBuilder sb = new StringBuilder(7).append("#"); + if (chars.length == 6) { + sb.append(chars); + } else if (chars.length == 3) { + for (char c : chars) { + sb.append(c).append(c); + } + } else { + throw new IllegalArgumentException("unparsable color string: \"" + input + "\""); + } + final String formattedHexColor = sb.toString(); + if (isParsableValidHexColorString(formattedHexColor)) { + return formattedHexColor; + } else { + throw new IllegalArgumentException("\"" + input + "\" is not a valid color string. Result of tried normalizing: " + formattedHexColor); + } + } + + /** + * Checking for {@link Color#parseColor(String)} being able to parse the input is the important part because we don't know the implementation and rely on it to be able to parse the color. + * + * @return true, if the input starts with a hash followed by 6 characters of hex numbers and is parsable by {@link Color#parseColor(String)}. + */ + private static boolean isParsableValidHexColorString(@NonNull String input) { + try { + Color.parseColor(input); + return input.matches("#[a-fA-F0-9]{6}"); + } catch (Exception e) { + return false; + } + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/util/DisplayUtils.java b/app/src/main/java/it/niedermann/owncloud/notes/util/DisplayUtils.java index a4fd4c41..50931618 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/util/DisplayUtils.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/util/DisplayUtils.java @@ -26,6 +26,7 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.style.MetricAffectingSpan; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -33,6 +34,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; + +import static it.niedermann.owncloud.notes.util.ColorUtil.isColorDark; public class DisplayUtils { @@ -40,7 +44,7 @@ public class DisplayUtils { } - public static Spannable searchAndColor(Spannable spannable, CharSequence searchText, Context context, @Nullable Integer current) { + public static Spannable searchAndColor(Spannable spannable, CharSequence searchText, @NonNull Context context, @Nullable Integer current, @ColorInt int mainColor, @ColorInt int textColor) { CharSequence text = spannable.toString(); Object[] spansToRemove = spannable.getSpans(0, text.length(), Object.class); @@ -60,7 +64,7 @@ public class DisplayUtils { while (m.find()) { int start = m.start(); int end = m.end(); - spannable.setSpan(new SearchSpan(context, (current != null && i == current)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + spannable.setSpan(new SearchSpan(context, mainColor, textColor, (current != null && i == current)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); i++; } @@ -71,19 +75,51 @@ public class DisplayUtils { static class SearchSpan extends MetricAffectingSpan { private final boolean current; - private final int bgColorPrimary; - private final int bgColorSecondary; - - SearchSpan(Context context, boolean current) { + @NonNull + Context context; + @ColorInt + private final int mainColor; + @ColorInt + private final int textColor; + @ColorInt + private final int highlightColor; + + SearchSpan(@NonNull Context context, @ColorInt int mainColor, @ColorInt int textColor, boolean current) { + this.context = context; + this.mainColor = mainColor; + this.textColor = textColor; this.current = current; - this.bgColorPrimary = context.getResources().getColor(R.color.bg_search_primary); - this.bgColorSecondary = context.getResources().getColor(R.color.bg_search_secondary); + this.highlightColor = context.getResources().getColor(R.color.bg_highlighted); } @Override public void updateDrawState(TextPaint tp) { - tp.bgColor = current ? bgColorPrimary : bgColorSecondary; - tp.setColor(current ? (getForeground(Integer.toHexString(tp.bgColor)) ? Color.WHITE : Color.BLACK) : bgColorPrimary); + if (current) { + if (Notes.isDarkThemeActive(context)) { + if (isColorDark(mainColor)) { + tp.bgColor = Color.WHITE; + tp.setColor(mainColor); + } else { + tp.bgColor = mainColor; + tp.setColor(Color.BLACK); + } + } else { + if (isColorDark(mainColor)) { + tp.bgColor = mainColor; + tp.setColor(Color.WHITE); + } else { + if (ColorUtil.contrastRatioIsSufficient(mainColor, highlightColor)) { + tp.bgColor = highlightColor; + } else { + tp.bgColor = Color.BLACK; + } + tp.setColor(mainColor); + } + } + } else { + tp.bgColor = highlightColor; + tp.setColor(BrandingUtil.getSecondaryForegroundColorDependingOnTheme(context, mainColor)); + } tp.setFakeBoldText(true); } @@ -92,12 +128,4 @@ public class DisplayUtils { tp.setFakeBoldText(true); } } - - public static boolean getForeground(String backgroundColorHex) { - return ((float) ( - 0.2126 * Integer.valueOf(backgroundColorHex.substring(1, 3), 16) - + 0.7152 * Integer.valueOf(backgroundColorHex.substring(3, 5), 16) - + 0.0722 * Integer.valueOf(backgroundColorHex.substring(5, 7), 16) - ) < 140); - } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/util/MarkDownUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/util/MarkDownUtil.java index 9d9abe95..66f89280 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/util/MarkDownUtil.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/util/MarkDownUtil.java @@ -65,7 +65,7 @@ public class MarkDownUtil { darkTheme ? R.color.widget_fg_dark_theme : R.color.widget_fg_default, null)) .setTodoDoneColor(ResourcesCompat.getColor(context.getResources(), darkTheme ? R.color.widget_fg_dark_theme : R.color.widget_fg_default, null)) - .setLinkFontColor(ResourcesCompat.getColor(context.getResources(), R.color.primary, null)) + .setLinkFontColor(ResourcesCompat.getColor(context.getResources(), R.color.defaultBrand, null)) .setRxMDImageLoader(new NotesImageLoader(context)) .setDefaultImageSize(400, 300); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/util/SSOUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/util/SSOUtil.java index 5089f14d..77fa0d2c 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/util/SSOUtil.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/util/SSOUtil.java @@ -45,9 +45,7 @@ public class SSOUtil { try { SingleAccountHelper.getCurrentSingleSignOnAccount(context); return true; - } catch (NextcloudFilesAppAccountNotFoundException e) { - return false; - } catch (NoCurrentAccountSelectedException e) { + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { return false; } } diff --git a/app/src/main/res/animator/appbar_elevation_off.xml b/app/src/main/res/animator/appbar_elevation_off.xml new file mode 100644 index 00000000..d24dcb34 --- /dev/null +++ b/app/src/main/res/animator/appbar_elevation_off.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <objectAnimator + android:propertyName="elevation" + android:valueTo="0dp" + android:valueType="floatType" /> + </item> +</selector> diff --git a/app/src/main/res/animator/appbar_elevation_on.xml b/app/src/main/res/animator/appbar_elevation_on.xml new file mode 100644 index 00000000..6bd52cf3 --- /dev/null +++ b/app/src/main/res/animator/appbar_elevation_on.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="PrivateResource"> + <item> + <objectAnimator + android:propertyName="elevation" + android:valueTo="@dimen/design_appbar_elevation" + android:valueType="floatType" /> + </item> +</selector> diff --git a/app/src/main/res/drawable-night/border.xml b/app/src/main/res/drawable-night/border.xml index 48ec3e9b..ab9d52ea 100644 --- a/app/src/main/res/drawable-night/border.xml +++ b/app/src/main/res/drawable-night/border.xml @@ -1,6 +1,8 @@ -<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> <solid android:color="@color/category_background" /> - <stroke android:width="1dip" android:color="@color/category_border"/> - <corners - android:radius="4dp" /> + <stroke + android:width="1dip" + android:color="@color/category_border" /> + <corners android:radius="4dp" /> </shape>
\ No newline at end of file diff --git a/app/src/main/res/drawable/border.xml b/app/src/main/res/drawable/border.xml index 48ec3e9b..319814a6 100644 --- a/app/src/main/res/drawable/border.xml +++ b/app/src/main/res/drawable/border.xml @@ -1,6 +1,8 @@ -<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > - <solid android:color="@color/category_background" /> - <stroke android:width="1dip" android:color="@color/category_border"/> - <corners - android:radius="4dp" /> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@android:color/transparent" /> + <stroke + android:width="1dip" + android:color="@color/category_border" /> + <corners android:radius="4dp" /> </shape>
\ No newline at end of file diff --git a/app/src/main/res/drawable/check.xml b/app/src/main/res/drawable/check.xml new file mode 100644 index 00000000..4c6c6b76 --- /dev/null +++ b/app/src/main/res/drawable/check.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/area"> + <shape android:shape="oval"> + <solid android:color="@color/defaultBrand" /> + <stroke + android:width="1dp" + android:color="@android:color/white" /> + </shape> + </item> + <item android:drawable="@drawable/ic_check_white_24dp" /> +</layer-list>
\ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml b/app/src/main/res/drawable/ic_arrow_back_grey600_24dp.xml index cc2b6f53..99f77385 100644 --- a/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml +++ b/app/src/main/res/drawable/ic_arrow_back_grey600_24dp.xml @@ -1,5 +1,5 @@ <vector android:autoMirrored="true" android:height="24dp" - android:tint="#FFFFFF" android:viewportHeight="24.0" + android:tint="#757575" 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="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/> </vector> diff --git a/app/src/main/res/drawable/ic_baseline_menu_24.xml b/app/src/main/res/drawable/ic_baseline_menu_24.xml new file mode 100644 index 00000000..be2baed8 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_menu_24.xml @@ -0,0 +1,5 @@ +<vector android:autoMirrored="true" android:height="24dp" + android:tint="#757575" android:viewportHeight="24" + android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="@android:color/white" android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_check_white_24dp.xml b/app/src/main/res/drawable/ic_check_white_24dp.xml new file mode 100644 index 00000000..65eff3fd --- /dev/null +++ b/app/src/main/res/drawable/ic_check_white_24dp.xml @@ -0,0 +1,5 @@ +<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="#FF000000" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_color_lens_grey600_24dp.xml b/app/src/main/res/drawable/ic_color_lens_grey600_24dp.xml new file mode 100644 index 00000000..33db0157 --- /dev/null +++ b/app/src/main/res/drawable/ic_color_lens_grey600_24dp.xml @@ -0,0 +1,5 @@ +<vector android:autoMirrored="true" android:height="24dp" + android:tint="#757575" 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,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5L16,16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zM6.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zM9.5,8C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zM14.5,8c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zM17.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_delete_white_24dp.xml b/app/src/main/res/drawable/ic_delete_white_24dp.xml deleted file mode 100644 index 4d020aff..00000000 --- a/app/src/main/res/drawable/ic_delete_white_24dp.xml +++ /dev/null @@ -1,5 +0,0 @@ -<vector android:autoMirrored="true" 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="#FFFFFFFF" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/> -</vector> diff --git a/app/src/main/res/drawable/ic_edit_white_24dp.xml b/app/src/main/res/drawable/ic_edit_grey600_24dp.xml index 5af858dd..1c520a99 100644 --- a/app/src/main/res/drawable/ic_edit_white_24dp.xml +++ b/app/src/main/res/drawable/ic_edit_grey600_24dp.xml @@ -1,5 +1,5 @@ <vector android:autoMirrored="true" android:height="24dp" - android:tint="#FFFFFF" android:viewportHeight="24.0" + android:tint="#757575" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> <path android:fillColor="#FFFFFFFF" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/> </vector> diff --git a/app/src/main/res/drawable/ic_eye_white_24dp.xml b/app/src/main/res/drawable/ic_eye_grey600_24dp.xml index 2386233f..64cf84b1 100644 --- a/app/src/main/res/drawable/ic_eye_white_24dp.xml +++ b/app/src/main/res/drawable/ic_eye_grey600_24dp.xml @@ -1,5 +1,5 @@ <vector android:autoMirrored="true" android:height="24dp" - android:tint="#FFFFFF" android:viewportHeight="24.0" + android:tint="#757575" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> <path android:fillColor="#FFFFFFFF" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/> </vector> diff --git a/app/src/main/res/drawable/ic_launcher_foreground_full.xml b/app/src/main/res/drawable/ic_launcher_foreground_full.xml new file mode 100644 index 00000000..e3e1f1ac --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground_full.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="32" + android:viewportHeight="32"> + <group android:translateX="0" + android:translateY="0"> + <path + android:fillColor="#fff" + android:pathData="m24.484 3.5156c-1.0237 0-2.0471 0.38887-2.8281 1.1699l5.6582 5.6582c1.5621-1.5621 1.5621-4.0961 0-5.6582-0.78105-0.78105-1.8064-1.1699-2.8301-1.1699zm-4.2422 2.584-12.02 12.021 5.6562 5.6562 12.021-12.02-5.6582-5.6582zm-13.436 13.436-2.1211 7.7793 7.7793-2.1211-5.6582-5.6582z" /> + </group> +</vector>
\ No newline at end of file diff --git a/app/src/main/res/drawable/ic_remove_red_eye_grey_24dp.xml b/app/src/main/res/drawable/ic_remove_red_eye_grey_24dp.xml index dc85cfe3..d9b1702d 100644 --- a/app/src/main/res/drawable/ic_remove_red_eye_grey_24dp.xml +++ b/app/src/main/res/drawable/ic_remove_red_eye_grey_24dp.xml @@ -1,9 +1,9 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> <path android:fillColor="#FF757575" - android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/> + android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" /> </vector> diff --git a/app/src/main/res/drawable/ic_search_grey600_24dp.xml b/app/src/main/res/drawable/ic_search_grey600_24dp.xml new file mode 100644 index 00000000..d46f2cb3 --- /dev/null +++ b/app/src/main/res/drawable/ic_search_grey600_24dp.xml @@ -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="#FF757575" + android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_search_white_24dp.xml b/app/src/main/res/drawable/ic_search_white_24dp.xml deleted file mode 100644 index 3e71206e..00000000 --- a/app/src/main/res/drawable/ic_search_white_24dp.xml +++ /dev/null @@ -1,5 +0,0 @@ -<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="#FFFFFFFF" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> -</vector> diff --git a/app/src/main/res/drawable/ic_send_white_24dp.xml b/app/src/main/res/drawable/ic_send_grey600_24dp.xml index 97dd32a4..08fdc123 100644 --- a/app/src/main/res/drawable/ic_send_white_24dp.xml +++ b/app/src/main/res/drawable/ic_send_grey600_24dp.xml @@ -1,5 +1,5 @@ <vector android:autoMirrored="true" android:height="24dp" - android:tint="#FFFFFF" android:viewportHeight="24.0" + android:tint="#757575" 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="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/> </vector> diff --git a/app/src/main/res/drawable/splashscreen.xml b/app/src/main/res/drawable/splashscreen.xml index e61ed69b..e6856ad7 100644 --- a/app/src/main/res/drawable/splashscreen.xml +++ b/app/src/main/res/drawable/splashscreen.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@drawable/ic_launcher_background" /> + <item android:drawable="@color/defaultBrand" /> <item> <bitmap diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 3aac5851..8e6c0a2f 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -1,31 +1,37 @@ <?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" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" android:orientation="vertical"> - <androidx.appcompat.widget.Toolbar - android:id="@+id/toolbar" + <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" - android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" - android:theme="@style/toolbarStyle" - app:contentInsetStartWithNavigation="0dp" - app:elevation="4dp" - app:navigationIcon="@drawable/ic_arrow_back_white_24dp" - app:titleMarginStart="0dp" - tools:title="@string/simple_about" /> + android:layout_height="wrap_content"> - <com.google.android.material.tabs.TabLayout - android:id="@+id/tabs" - 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" + android:background="?attr/colorPrimary" + app:contentInsetStartWithNavigation="0dp" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + app:titleMarginStart="0dp" + tools:title="@string/simple_about" /> + + <com.google.android.material.tabs.TabLayout + android:id="@+id/tabs" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:tabIndicatorColor="@color/defaultBrand" + android:background="?attr/colorPrimary" /> + </com.google.android.material.appbar.AppBarLayout> <androidx.viewpager.widget.ViewPager android:id="@+id/pager" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:background="?attr/colorPrimary" /> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_edit.xml b/app/src/main/res/layout/activity_edit.xml index c01930b7..e7da04b0 100644 --- a/app/src/main/res/layout/activity_edit.xml +++ b/app/src/main/res/layout/activity_edit.xml @@ -1,26 +1,30 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout 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="match_parent" - xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical"> - <androidx.appcompat.widget.Toolbar - android:id="@+id/toolbar" + <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" - android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" - android:theme="@style/toolbarStyle" - app:contentInsetStartWithNavigation="0dp" - app:elevation="4dp" - app:navigationIcon="@drawable/ic_arrow_back_white_24dp" - app:titleMarginStart="0dp" - tools:title="Edit Sample note" /> + android:layout_height="wrap_content"> + + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + app:contentInsetStartWithNavigation="0dp" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + app:titleMarginStart="0dp" + tools:title="Edit Sample note" /> + </com.google.android.material.appbar.AppBarLayout> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragment_container_view" android:layout_width="match_parent" + android:background="?attr/colorPrimary" android:layout_height="match_parent" /> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_exception.xml b/app/src/main/res/layout/activity_exception.xml index 37dc4bcb..e1e25491 100644 --- a/app/src/main/res/layout/activity_exception.xml +++ b/app/src/main/res/layout/activity_exception.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="?attr/colorPrimary" android:orientation="vertical"> <androidx.appcompat.widget.Toolbar @@ -11,7 +12,6 @@ android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" - android:theme="@style/toolbarStyle" app:contentInsetStartWithNavigation="0dp" app:elevation="4dp" app:titleMarginStart="0dp" @@ -30,12 +30,12 @@ android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginStart="@dimen/spacer_2x" - android:layout_marginLeft="16dp" - android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" + android:layout_marginLeft="@dimen/spacer_2x" + android:layout_marginEnd="@dimen/spacer_2x" + android:layout_marginRight="@dimen/spacer_2x" android:layout_weight="1" android:background="@color/bg_highlighted" - android:padding="8dp" + android:padding="@dimen/spacer_1x" android:scrollbars="horizontal|vertical" android:textIsSelectable="true" android:typeface="monospace" @@ -45,7 +45,7 @@ style="?android:buttonBarStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="16dp" + android:layout_margin="@dimen/spacer_2x" android:gravity="end" android:orientation="horizontal" android:weightSum="1.0"> @@ -56,7 +56,8 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight=".5" - android:text="@string/simple_close" /> + android:text="@string/simple_close" + android:textColor="@color/defaultBrand" /> <Button android:id="@+id/copy" @@ -66,7 +67,7 @@ android:layout_marginTop="6dp" android:layout_marginBottom="6dp" android:layout_weight=".5" - android:background="@color/primary" + android:background="@color/defaultBrand" android:foreground="?attr/selectableItemBackground" android:text="@string/simple_copy" android:textColor="@color/fg_contrast" diff --git a/app/src/main/res/layout/activity_manage_accounts.xml b/app/src/main/res/layout/activity_manage_accounts.xml new file mode 100644 index 00000000..60e684cf --- /dev/null +++ b/app/src/main/res/layout/activity_manage_accounts.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout 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="match_parent" + android:background="?attr/colorPrimary" + android:orientation="vertical"> + + <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" + android:background="?attr/colorPrimary" + app:contentInsetStartWithNavigation="0dp" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + app:title="@string/manage_accounts" + app:titleMarginStart="0dp" /> + </com.google.android.material.appbar.AppBarLayout> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/accounts" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/item_account_choose" /> + +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_note_list_configuration.xml b/app/src/main/res/layout/activity_note_list_configuration.xml index bf14a9fe..061784e3 100644 --- a/app/src/main/res/layout/activity_note_list_configuration.xml +++ b/app/src/main/res/layout/activity_note_list_configuration.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <androidx.recyclerview.widget.RecyclerView 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" diff --git a/app/src/main/res/layout/activity_notes_list_view.xml b/app/src/main/res/layout/activity_notes_list_view.xml index 2915f416..1b022607 100644 --- a/app/src/main/res/layout/activity_notes_list_view.xml +++ b/app/src/main/res/layout/activity_notes_list_view.xml @@ -11,23 +11,94 @@ android:layout_height="match_parent" android:orientation="vertical"> - <androidx.appcompat.widget.Toolbar - android:id="@+id/toolbar" + <com.google.android.material.appbar.AppBarLayout + android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" - android:theme="@style/toolbarStyle" - app:contentInsetStartWithNavigation="0dp" - app:elevation="4dp" - app:titleMarginStart="0dp" - tools:navigationIcon="@drawable/ic_arrow_back_white_24dp" - tools:title="@string/app_name" /> + app:elevation="0dp"> + + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" + app:contentInsetStartWithNavigation="0dp" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + app:titleMarginStart="0dp" + tools:title="@string/app_name"> + + <androidx.appcompat.widget.SearchView + android:id="@+id/search_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </androidx.appcompat.widget.Toolbar> + + <com.google.android.material.card.MaterialCardView + android:id="@+id/home_toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacer_1x" + android:layout_marginLeft="@dimen/spacer_1x" + android:layout_marginTop="@dimen/design_appbar_elevation" + android:layout_marginEnd="@dimen/spacer_1x" + android:layout_marginRight="@dimen/spacer_1x" + android:layout_marginBottom="@dimen/design_appbar_elevation" + app:cardCornerRadius="@dimen/spacer_1x" + app:cardElevation="6dp" + app:strokeWidth="0dp"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <androidx.appcompat.widget.AppCompatImageButton + android:id="@+id/menu_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackgroundBorderless" + android:paddingStart="@dimen/spacer_1x" + android:paddingLeft="@dimen/spacer_1x" + android:paddingTop="@dimen/spacer_2x" + android:paddingEnd="@dimen/spacer_1x" + android:paddingRight="@dimen/spacer_1x" + android:paddingBottom="@dimen/spacer_2x" + android:tint="?attr/colorAccent" + app:srcCompat="@drawable/ic_baseline_menu_24" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/search_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginStart="@dimen/spacer_1x" + android:layout_marginLeft="@dimen/spacer_1x" + android:layout_marginEnd="@dimen/spacer_1x" + android:layout_marginRight="@dimen/spacer_1x" + android:layout_weight="1" + android:ellipsize="end" + android:gravity="start" + android:lines="1" + android:textSize="16sp" + tools:text="@string/search_in_all" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/launchAccountSwitcher" + android:layout_width="?attr/actionBarSize" + android:layout_height="?attr/actionBarSize" + android:layout_gravity="center_vertical|end" + android:background="?attr/selectableItemBackgroundBorderless" + android:padding="12dp" + app:srcCompat="@drawable/ic_account_circle_grey_24dp" /> + </LinearLayout> + + </com.google.android.material.card.MaterialCardView> + </com.google.android.material.appbar.AppBarLayout> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/swiperefreshlayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/bg_normal" tools:context="it.niedermann.owncloud.notes.android.activity.NotesListViewActivity"> <FrameLayout @@ -43,13 +114,14 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:indeterminateTint="@color/defaultBrand" + tools:targetApi="lollipop" tools:visibility="gone" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/bg_highlighted" android:scrollbars="vertical" tools:listitem="@layout/item_notes_list_note_item" /> </FrameLayout> @@ -61,7 +133,6 @@ android:id="@+id/fab_create" style="@style/fab" android:title="@string/action_create" - app:backgroundTint="@color/primary" - app:rippleColor="@color/primary_dark" + app:backgroundTint="@color/defaultBrand" app:srcCompat="@drawable/ic_add_white_24dp" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_preferences.xml b/app/src/main/res/layout/activity_preferences.xml index a8e43cc1..10b8089e 100644 --- a/app/src/main/res/layout/activity_preferences.xml +++ b/app/src/main/res/layout/activity_preferences.xml @@ -5,21 +5,25 @@ android:layout_height="match_parent" android:orientation="vertical"> - <androidx.appcompat.widget.Toolbar - android:id="@+id/toolbar" + <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" - android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" - android:theme="@style/toolbarStyle" - app:contentInsetStartWithNavigation="0dp" - app:elevation="4dp" - app:navigationIcon="@drawable/ic_arrow_back_white_24dp" - app:title="@string/action_settings" - app:titleMarginStart="0dp" /> + android:layout_height="wrap_content"> + + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + app:contentInsetStartWithNavigation="0dp" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + app:title="@string/action_settings" + app:titleMarginStart="0dp" /> + </com.google.android.material.appbar.AppBarLayout> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragment_container_view" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:background="?attr/colorPrimary" /> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/dialog_account_switcher.xml b/app/src/main/res/layout/dialog_account_switcher.xml new file mode 100644 index 00000000..f5260947 --- /dev/null +++ b/app/src/main/res/layout/dialog_account_switcher.xml @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout 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:orientation="vertical"> + + <LinearLayout + android:id="@+id/accountLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:orientation="horizontal" + android:padding="@dimen/spacer_3x"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/currentAccountItemAvatar" + android:layout_width="36dp" + android:layout_height="36dp" + android:contentDescription="@null" + android:focusable="false" + android:scaleType="center" + app:srcCompat="@drawable/ic_account_circle_grey_24dp" /> + + <TextView + android:id="@+id/accountItemLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="@dimen/spacer_2x" + android:layout_marginLeft="@dimen/spacer_2x" + android:layout_marginEnd="@dimen/spacer_2x" + android:layout_marginRight="@dimen/spacer_2x" + android:layout_weight="1" + android:ellipsize="middle" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem" + android:textColor="?android:textColorPrimary" + tools:text="@tools:sample/full_names" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/check" + android:layout_width="36dp" + android:layout_height="36dp" + android:contentDescription="@null" + android:focusable="false" + android:scaleType="center" + app:srcCompat="@drawable/check" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@color/fg_default_high" /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/accounts_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="0dp" + android:scrollbarStyle="outsideOverlay" + android:scrollbars="vertical" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:itemCount="3" + tools:listitem="@layout/item_account_choose" /> + + <LinearLayout + android:id="@+id/add_account" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:orientation="horizontal" + android:paddingStart="@dimen/spacer_3x" + android:paddingLeft="@dimen/spacer_3x" + android:paddingTop="@dimen/spacer_1x" + android:paddingEnd="@dimen/spacer_3x" + android:paddingRight="@dimen/spacer_3x" + android:paddingBottom="@dimen/spacer_1x"> + + <androidx.appcompat.widget.AppCompatImageView + android:layout_width="36dp" + android:layout_height="36dp" + android:contentDescription="@null" + android:focusable="false" + android:scaleType="center" + app:srcCompat="@drawable/ic_person_add_grey600_24dp" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="@dimen/spacer_2x" + android:layout_marginLeft="@dimen/spacer_2x" + android:ellipsize="middle" + android:singleLine="true" + android:text="@string/add_account" + android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem" + android:textColor="?android:textColorPrimary" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/manage_accounts" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/spacer_2x" + android:background="?attr/selectableItemBackground" + android:orientation="horizontal" + android:paddingStart="@dimen/spacer_3x" + android:paddingLeft="@dimen/spacer_3x" + android:paddingTop="@dimen/spacer_1x" + android:paddingEnd="@dimen/spacer_3x" + android:paddingRight="@dimen/spacer_3x" + android:paddingBottom="@dimen/spacer_1x"> + + <androidx.appcompat.widget.AppCompatImageView + android:layout_width="36dp" + android:layout_height="36dp" + android:contentDescription="@null" + android:focusable="false" + android:scaleType="center" + app:srcCompat="@drawable/ic_settings_grey600_24dp" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="@dimen/spacer_2x" + android:layout_marginLeft="@dimen/spacer_2x" + android:ellipsize="middle" + android:singleLine="true" + android:text="@string/manage_accounts" + android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem" + android:textColor="?android:textColorPrimary" /> + + </LinearLayout> +</LinearLayout> + diff --git a/app/src/main/res/layout/dialog_change_category.xml b/app/src/main/res/layout/dialog_change_category.xml index a73a32e9..86243c23 100644 --- a/app/src/main/res/layout/dialog_change_category.xml +++ b/app/src/main/res/layout/dialog_change_category.xml @@ -5,21 +5,16 @@ android:id="@+id/editCategoryLayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + android:padding="?attr/dialogPreferredPadding"> - <com.google.android.material.textfield.TextInputLayout + <EditText + android:id="@+id/search" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="16dp"> - - <EditText - android:id="@+id/search" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/change_category_title" - android:importantForAutofill="no" - android:inputType="text" /> - </com.google.android.material.textfield.TextInputLayout> + android:hint="@string/change_category_title" + android:importantForAutofill="no" + android:inputType="text" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" diff --git a/app/src/main/res/layout/dialog_exception.xml b/app/src/main/res/layout/dialog_exception.xml index c5327464..875605ac 100644 --- a/app/src/main/res/layout/dialog_exception.xml +++ b/app/src/main/res/layout/dialog_exception.xml @@ -47,7 +47,7 @@ android:layout_marginBottom="@dimen/spacer_2x" android:layout_weight="1" android:background="@color/bg_highlighted" - android:padding="8dp" + android:padding="@dimen/spacer_1x" android:scrollbars="horizontal|vertical" android:textIsSelectable="true" android:typeface="monospace" diff --git a/app/src/main/res/layout/drawer_layout.xml b/app/src/main/res/layout/drawer_layout.xml index 74419fd9..ec445d1c 100644 --- a/app/src/main/res/layout/drawer_layout.xml +++ b/app/src/main/res/layout/drawer_layout.xml @@ -5,7 +5,6 @@ android:id="@+id/drawerLayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:fitsSystemWindows="true" tools:openDrawer="left"> <include @@ -23,7 +22,8 @@ <androidx.core.widget.NestedScrollView android:id="@+id/scrollView" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:background="?attr/colorPrimary"> <LinearLayout android:layout_width="match_parent" @@ -33,120 +33,53 @@ <RelativeLayout android:id="@+id/header_view" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?attr/colorPrimary" - android:theme="@style/ThemeOverlay.AppCompat.Dark"> - - <androidx.appcompat.widget.AppCompatImageView - android:layout_width="match_parent" - android:layout_height="164dp" - android:contentDescription="@null" - android:scaleType="centerCrop" - app:srcCompat="@drawable/background" /> + android:layout_height="@dimen/drawer_header_height" + android:background="@color/defaultBrand"> <androidx.appcompat.widget.AppCompatImageView - android:id="@+id/current_account_image" - android:layout_width="@dimen/avatar_size" - android:layout_height="@dimen/avatar_size" - android:layout_gravity="center_vertical" - android:layout_marginStart="@dimen/header_padding" - android:layout_marginLeft="@dimen/header_padding" - android:layout_marginTop="46dp" - android:contentDescription="@string/app_name" - app:srcCompat="@mipmap/ic_launcher" /> + android:id="@+id/logo" + android:layout_width="@dimen/drawer_header_logo_size" + android:layout_height="@dimen/drawer_header_logo_size" + android:layout_centerVertical="true" + android:layout_margin="@dimen/spacer_2x" + android:gravity="center" + app:srcCompat="@drawable/ic_launcher_foreground_full" /> <TextView android:id="@+id/app_name" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@id/current_account_image" - android:layout_marginTop="6dp" - android:layout_marginStart="@dimen/header_padding" - android:layout_marginLeft="@dimen/header_padding" - android:layout_marginEnd="@dimen/header_padding" - android:layout_marginRight="@dimen/header_padding" - android:ellipsize="end" - android:shadowColor="@android:color/black" - android:shadowDx="0.5" - android:shadowDy="0" - android:shadowRadius="2" - android:text="@string/app_name_long" - android:textColor="@android:color/white" - android:textSize="14sp" - android:textStyle="bold" /> - - <TextView - android:id="@+id/account" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@+id/app_name" - android:layout_marginStart="@dimen/header_padding" - android:layout_marginLeft="@dimen/header_padding" - android:layout_marginEnd="@dimen/widget_margin" - android:layout_marginRight="@dimen/widget_margin" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/logo" + android:layout_toRightOf="@id/logo" android:ellipsize="end" - android:maxLines="1" - android:shadowColor="@android:color/black" - android:layout_alignParentStart="true" - android:layout_toStartOf="@id/account_arrow" - android:shadowDx="0.5" - android:shadowDy="0" - android:shadowRadius="2" + android:fontFamily="sans-serif-light" + android:gravity="center_vertical" + android:text="@string/app_name" android:textColor="@android:color/white" - android:textSize="12sp" - tools:text="user@nextcloud.example.comuser@nextcloud.example.comuser@nextcloud.example.com" - android:layout_alignParentLeft="true" - android:layout_toLeftOf="@id/account_arrow" /> - - <androidx.appcompat.widget.AppCompatImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/account_arrow" - app:srcCompat="@drawable/ic_arrow_drop_down_white_24dp" - android:layout_alignParentEnd="true" - android:layout_alignBottom="@+id/account" - android:layout_marginEnd="@dimen/header_padding" - android:layout_alignParentRight="true" - android:layout_marginRight="@dimen/header_padding" /> + android:textSize="24sp" /> </RelativeLayout> - <LinearLayout - android:id="@+id/accountNavigation" + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/navigationList" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical"> - - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/navigationList" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingBottom="8dp" - app:layoutManager="LinearLayoutManager" - tools:listitem="@layout/item_navigation" /> - - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/navigationMenu" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingBottom="8dp" - app:layoutManager="LinearLayoutManager" - tools:listitem="@layout/item_navigation" /> - </LinearLayout> - - <LinearLayout - android:id="@+id/accountChooser" + android:paddingTop="@dimen/spacer_1x" + android:paddingBottom="@dimen/spacer_1x" + app:layoutManager="LinearLayoutManager" + tools:itemCount="6" + tools:listitem="@layout/item_navigation" /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/navigationMenu" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:orientation="vertical" - android:visibility="gone"/> + android:paddingTop="@dimen/spacer_1x" + android:paddingBottom="@dimen/spacer_1x" + app:layoutManager="LinearLayoutManager" + tools:itemCount="3" + tools:listitem="@layout/item_navigation" /> </LinearLayout> - </androidx.core.widget.NestedScrollView> - </com.google.android.material.navigation.NavigationView> - </androidx.drawerlayout.widget.DrawerLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_about_license_tab.xml b/app/src/main/res/layout/fragment_about_license_tab.xml index c57c88ed..6afaa763 100644 --- a/app/src/main/res/layout/fragment_about_license_tab.xml +++ b/app/src/main/res/layout/fragment_about_license_tab.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <ScrollView 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"> @@ -22,17 +23,19 @@ android:padding="10dp" android:text="@string/about_app_license" /> - <Button + <com.google.android.material.button.MaterialButton android:id="@+id/about_app_license_button" + style="@style/Widget.MaterialComponents.Button" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/about_app_license_button" /> + android:text="@string/about_app_license_button" + app:backgroundTint="@color/defaultBrand" /> <TextView style="?android:attr/listSeparatorTextViewStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/spacer_2x" android:text="@string/about_icons_disclaimer_title" /> <TextView diff --git a/app/src/main/res/layout/fragment_note_edit.xml b/app/src/main/res/layout/fragment_note_edit.xml index 954351b0..1c0417d2 100644 --- a/app/src/main/res/layout/fragment_note_edit.xml +++ b/app/src/main/res/layout/fragment_note_edit.xml @@ -3,7 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:background="?attr/colorPrimary"> <ScrollView android:id="@+id/scrollView" @@ -17,12 +18,12 @@ android:id="@+id/editContent" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@null" android:ems="10" android:gravity="top" android:inputType="textMultiLine|textCapSentences" - android:padding="16dp" + android:padding="@dimen/spacer_2x" android:textColor="@color/fg_default" + android:theme="@style/textViewStyle" tools:text="@tools:sample/lorem/random" /> </ScrollView> @@ -32,6 +33,7 @@ android:layout_gravity="bottom|end" android:translationY="-56dp" android:visibility="gone" + app:backgroundTint="@color/defaultBrand" app:fabSize="mini" app:srcCompat="@drawable/ic_keyboard_arrow_up_white_24dp" tools:visibility="visible" /> @@ -41,6 +43,7 @@ style="@style/fab" android:layout_gravity="bottom|end" android:visibility="gone" + app:backgroundTint="@color/defaultBrand" app:fabSize="mini" app:srcCompat="@drawable/ic_keyboard_arrow_down_white_24dp" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/fragment_note_preview.xml b/app/src/main/res/layout/fragment_note_preview.xml index 0e81b33e..d841f3c2 100644 --- a/app/src/main/res/layout/fragment_note_preview.xml +++ b/app/src/main/res/layout/fragment_note_preview.xml @@ -3,13 +3,13 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:background="?attr/colorPrimary"> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/swiperefreshlayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/bg_normal" tools:context="it.niedermann.owncloud.notes.android.activity.NotesListViewActivity" tools:ignore="MergeRootFrame"> @@ -24,11 +24,11 @@ android:id="@+id/single_note_content" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/bg_normal" - android:padding="16dp" + android:padding="@dimen/spacer_2x" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@color/fg_default" android:textIsSelectable="true" + android:theme="@style/textViewStyle" tools:text="@tools:sample/lorem/random" /> </ScrollView> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> @@ -39,6 +39,7 @@ android:layout_gravity="bottom|end" android:translationY="-56dp" android:visibility="gone" + app:backgroundTint="@color/defaultBrand" app:fabSize="mini" app:srcCompat="@drawable/ic_keyboard_arrow_up_white_24dp" tools:visibility="visible" /> @@ -48,6 +49,7 @@ style="@style/fab" android:layout_gravity="bottom|end" android:visibility="gone" + app:backgroundTint="@color/defaultBrand" app:fabSize="mini" app:srcCompat="@drawable/ic_keyboard_arrow_down_white_24dp" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/item_account.xml b/app/src/main/res/layout/item_account.xml index ac14985b..18c6f52e 100644 --- a/app/src/main/res/layout/item_account.xml +++ b/app/src/main/res/layout/item_account.xml @@ -6,8 +6,8 @@ android:background="?attr/selectableItemBackground" android:paddingLeft="6dp" android:paddingStart="6dp" - android:paddingRight="8dp" - android:paddingEnd="8dp"> + android:paddingRight="@dimen/spacer_1x" + android:paddingEnd="@dimen/spacer_1x"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/accountItemAvatar" diff --git a/app/src/main/res/layout/item_account_choose.xml b/app/src/main/res/layout/item_account_choose.xml index fc4899aa..33abe594 100644 --- a/app/src/main/res/layout/item_account_choose.xml +++ b/app/src/main/res/layout/item_account_choose.xml @@ -5,28 +5,60 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/selectableItemBackground" - android:padding="24dp" - android:orientation="horizontal"> + android:orientation="horizontal" + android:padding="@dimen/spacer_3x"> - <androidx.appcompat.widget.AppCompatImageView - android:id="@+id/accountItemAvatar" - android:layout_width="32dp" - android:layout_height="32dp" - android:scaleType="center" - android:focusable="false" - app:srcCompat="@drawable/ic_account_circle_grey_24dp" - android:contentDescription="@null" /> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/spacer_1hx" + android:layout_marginRight="@dimen/spacer_1hx"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/accountItemAvatar" + android:layout_width="36dp" + android:layout_height="36dp" + android:contentDescription="@null" + android:focusable="false" + android:scaleType="center" + app:srcCompat="@drawable/ic_account_circle_grey_24dp" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/currentAccountIndicator" + android:layout_width="12dp" + android:layout_height="12dp" + android:layout_gravity="end|bottom" + android:visibility="gone" + app:srcCompat="@drawable/check" + tools:visibility="visible" /> + </FrameLayout> <TextView android:id="@+id/accountItemLabel" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_width="wrap_content" android:layout_gravity="center" + android:layout_marginStart="@dimen/spacer_2x" + android:layout_marginLeft="@dimen/spacer_2x" + android:layout_marginEnd="@dimen/spacer_2x" + android:layout_marginRight="@dimen/spacer_2x" + android:layout_weight="1" android:ellipsize="middle" android:singleLine="true" - android:layout_marginLeft="16dp" - android:layout_marginStart="16dp" + android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem" android:textColor="?android:textColorPrimary" - tools:hint="Username"/> + tools:text="@tools:sample/full_names" /> + -</LinearLayout>
\ No newline at end of file + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/delete" + android:layout_width="36dp" + android:layout_height="36dp" + android:background="?attr/selectableItemBackgroundBorderless" + android:contentDescription="@null" + android:focusable="false" + android:scaleType="center" + android:visibility="gone" + app:srcCompat="@drawable/ic_delete_grey600_24dp" + tools:visibility="visible" /> +</LinearLayout> diff --git a/app/src/main/res/layout/item_category.xml b/app/src/main/res/layout/item_category.xml index 95c76732..b23a6e3f 100644 --- a/app/src/main/res/layout/item_category.xml +++ b/app/src/main/res/layout/item_category.xml @@ -7,14 +7,14 @@ android:background="?android:selectableItemBackground" android:clickable="true" android:focusable="true" - android:padding="16dp"> + android:padding="@dimen/spacer_2x"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" + android:layout_marginEnd="@dimen/spacer_2x" + android:layout_marginRight="@dimen/spacer_2x" android:contentDescription="@null" android:focusable="false" android:scaleType="center" @@ -36,8 +36,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|end" - android:layout_marginStart="16dp" - android:layout_marginLeft="16dp" + android:layout_marginStart="@dimen/spacer_2x" + android:layout_marginLeft="@dimen/spacer_2x" android:textColor="@color/fg_default" tools:text="23" /> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/item_navigation.xml b/app/src/main/res/layout/item_navigation.xml index 65fb4438..e1dc16e8 100644 --- a/app/src/main/res/layout/item_navigation.xml +++ b/app/src/main/res/layout/item_navigation.xml @@ -1,12 +1,13 @@ -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout 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:paddingStart="6dp" - android:paddingLeft="6dp" - android:paddingEnd="8dp" - android:paddingRight="8dp"> + android:gravity="center_vertical" + android:paddingStart="@dimen/spacer_1x" + android:paddingLeft="@dimen/spacer_1x" + android:paddingEnd="@dimen/spacer_1x" + android:paddingRight="@dimen/spacer_1x"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/navigationItemIcon" @@ -14,33 +15,26 @@ android:layout_height="44dp" android:contentDescription="@null" android:focusable="false" - android:padding="10dp" android:scaleType="center" app:srcCompat="@drawable/ic_folder_grey600_24dp" /> <TextView - android:id="@+id/navigationItemCount" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:padding="8dp" - android:textColor="?android:textColorPrimary" - tools:text="37" /> - - <TextView android:id="@+id/navigationItemLabel" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_marginStart="64dp" - android:layout_marginLeft="64dp" - android:layout_toStartOf="@id/navigationItemCount" - android:layout_toLeftOf="@id/navigationItemCount" + android:layout_marginStart="@dimen/spacer_1x" + android:layout_marginLeft="@dimen/spacer_1x" + android:layout_weight="1" android:ellipsize="end" android:lines="1" android:textColor="?android:textColorSecondary" - tools:text="Category 1" /> + tools:text="@tools:sample/lorem/random" /> -</RelativeLayout>
\ No newline at end of file + <TextView + android:id="@+id/navigationItemCount" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="@dimen/spacer_1x" + android:textColor="?android:textColorPrimary" + tools:text="37" /> +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/item_notes_list_note_item.xml b/app/src/main/res/layout/item_notes_list_note_item.xml index 61ad02fc..ba88af2a 100644 --- a/app/src/main/res/layout/item_notes_list_note_item.xml +++ b/app/src/main/res/layout/item_notes_list_note_item.xml @@ -12,8 +12,8 @@ android:layout_width="32dp" android:layout_height="32dp" android:layout_gravity="center_vertical" - android:layout_marginLeft="@dimen/button_padding" android:layout_marginStart="@dimen/button_padding" + android:layout_marginLeft="@dimen/button_padding" android:contentDescription="@string/menu_favorite" app:srcCompat="@drawable/ic_star_white_24dp" /> @@ -34,42 +34,43 @@ android:background="@drawable/list_item_background_selector"> <FrameLayout - android:layout_centerVertical="true" android:layout_width="wrap_content" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:layout_centerVertical="true"> <ImageView android:id="@+id/noteFavorite" - android:contentDescription="@string/menu_favorite" - android:padding="16dp" android:layout_width="wrap_content" android:layout_height="match_parent" - tools:src="@drawable/ic_star_yellow_24dp"/> + android:background="?attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/menu_favorite" + android:padding="@dimen/spacer_2x" + tools:src="@drawable/ic_star_yellow_24dp" /> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/noteStatus" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="12dp" android:layout_marginEnd="4dp" android:layout_marginRight="4dp" - android:layout_marginTop="12dp" - android:layout_gravity="center_vertical|end" android:baseline="14dp" app:srcCompat="@drawable/ic_sync_blue_18dp" /> </FrameLayout> <androidx.appcompat.widget.LinearLayoutCompat - android:layout_weight="1" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingTop="16dp" - android:paddingRight="16dp" - android:paddingEnd="16dp" - android:paddingLeft="0dp" + android:layout_weight="1" + android:orientation="vertical" android:paddingStart="0dp" - android:paddingBottom="16dp" - android:orientation="vertical"> + android:paddingLeft="0dp" + android:paddingTop="@dimen/spacer_2x" + android:paddingEnd="@dimen/spacer_2x" + android:paddingRight="@dimen/spacer_2x" + android:paddingBottom="@dimen/spacer_2x"> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" @@ -79,12 +80,12 @@ android:id="@+id/noteTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_weight="1" android:ellipsize="end" android:maxLines="1" - android:layout_weight="1" - android:textSize="@dimen/primary_font_size" android:textColor="?android:textColorPrimary" - tools:text="@tools:sample/lorem/random"/> + android:textSize="@dimen/primary_font_size" + tools:text="@tools:sample/lorem/random" /> </androidx.appcompat.widget.LinearLayoutCompat> @@ -96,34 +97,35 @@ android:id="@+id/noteExcerpt" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentLeft="true" android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:layout_weight="1" android:ellipsize="end" android:maxLines="1" - android:paddingBottom="1dp" android:paddingTop="1dp" - android:layout_weight="1" + android:paddingBottom="1dp" android:textSize="@dimen/secondary_font_size" - tools:text="@tools:sample/lorem/random"/> + tools:text="@tools:sample/lorem/random" /> <TextView android:id="@+id/noteCategory" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentRight="true" android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_marginStart="@dimen/spacer_1x" + android:layout_marginLeft="@dimen/spacer_1x" android:background="@drawable/border" android:maxLines="1" - android:paddingBottom="1dp" - android:paddingLeft="8dp" - android:paddingRight="8dp" + android:paddingLeft="@dimen/spacer_1x" android:paddingTop="1dp" - android:layout_marginStart="8dp" - android:layout_marginLeft="8dp" + android:paddingRight="@dimen/spacer_1x" + android:paddingBottom="1dp" android:singleLine="true" android:textColor="?android:textColorPrimary" android:textSize="@dimen/secondary_font_size" - tools:text="@string/category_work"/> + tools:maxLength="15" + tools:text="@tools:sample/lorem/random" /> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat> diff --git a/app/src/main/res/layout/item_notes_list_section_item.xml b/app/src/main/res/layout/item_notes_list_section_item.xml index 68af3740..6e20555c 100644 --- a/app/src/main/res/layout/item_notes_list_section_item.xml +++ b/app/src/main/res/layout/item_notes_list_section_item.xml @@ -1,28 +1,23 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - - <TextView - android:id="@+id/sectionTitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_alignParentEnd="false" - android:layout_alignParentRight="true" - android:layout_alignParentStart="true" - android:layout_alignParentLeft="true" - android:layout_alignParentTop="true" - android:layout_alignWithParentIfMissing="true" - android:background="@color/bg_normal" - android:ellipsize="end" - android:gravity="center_vertical" - android:paddingBottom="8dp" - android:paddingLeft="56dp" - android:paddingStart="56dp" - android:paddingRight="16dp" - android:paddingEnd="16dp" - android:paddingTop="48dp" - android:textColor="@color/fg_default_selection" - android:textSize="@dimen/secondary_font_size" - android:hint="@string/listview_updated_yesterday"/> -</RelativeLayout>
\ No newline at end of file +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/sectionTitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignWithParentIfMissing="true" + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:layout_alignParentEnd="false" + android:layout_alignParentRight="true" + android:background="@color/bg_normal" + android:ellipsize="end" + android:gravity="center_vertical" + android:hint="@string/listview_updated_yesterday" + android:paddingStart="56dp" + android:paddingLeft="56dp" + android:paddingTop="48dp" + android:paddingEnd="@dimen/spacer_2x" + android:paddingRight="@dimen/spacer_2x" + android:paddingBottom="@dimen/spacer_1x" + android:textColor="@color/fg_default_selection" + android:textSize="@dimen/secondary_font_size" />
\ No newline at end of file diff --git a/app/src/main/res/layout/item_pref.xml b/app/src/main/res/layout/item_pref.xml index 8237bdad..48d035ef 100644 --- a/app/src/main/res/layout/item_pref.xml +++ b/app/src/main/res/layout/item_pref.xml @@ -11,7 +11,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:contentDescription="@null" - android:padding="16dp" + android:padding="@dimen/spacer_2x" tools:src="@drawable/ic_settings_grey600_24dp" /> <LinearLayout @@ -22,7 +22,6 @@ android:orientation="vertical" tools:ignore="RtlSymmetry"> - <TextView android:id="@android:id/title" android:layout_width="wrap_content" @@ -49,6 +48,6 @@ android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="vertical" - android:padding="16dp" /> + android:padding="@dimen/spacer_2x" /> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/item_preference_category.xml b/app/src/main/res/layout/item_preference_category.xml new file mode 100644 index 00000000..4580a438 --- /dev/null +++ b/app/src/main/res/layout/item_preference_category.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@android:id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="56dp" + android:paddingLeft="56dp" + android:paddingTop="@dimen/spacer_3x" + android:paddingEnd="0dp" + android:paddingRight="0dp" + tools:text="@tools:sample/lorem" /> diff --git a/app/src/main/res/menu/menu_list_context_multiple.xml b/app/src/main/res/menu/menu_list_context_multiple.xml index b31ae774..9e44a799 100644 --- a/app/src/main/res/menu/menu_list_context_multiple.xml +++ b/app/src/main/res/menu/menu_list_context_multiple.xml @@ -11,14 +11,14 @@ <item android:id="@+id/menu_delete" - android:icon="@drawable/ic_delete_white_24dp" + android:icon="@drawable/ic_delete_grey600_24dp" android:orderInCategory="100" android:title="@string/menu_delete" app:showAsAction="ifRoom" /> <item android:id="@+id/menu_move" - android:icon="@drawable/ic_send_white_24dp" + android:icon="@drawable/ic_send_grey600_24dp" android:orderInCategory="110" android:title="@string/simple_move" app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/menu_list_view.xml b/app/src/main/res/menu/menu_list_view.xml deleted file mode 100644 index 70103414..00000000 --- a/app/src/main/res/menu/menu_list_view.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<menu 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" - tools:context="com.example.owncloudnotes.NotesListViewActivity"> - <item - android:id="@+id/search" - android:title="@string/action_search" - android:icon="@drawable/ic_search_white_24dp" - app:actionViewClass="androidx.appcompat.widget.SearchView" - app:showAsAction="collapseActionView|always" /> -</menu>
\ No newline at end of file diff --git a/app/src/main/res/menu/menu_note_activity.xml b/app/src/main/res/menu/menu_note_activity.xml index 311c5108..967bb4f0 100644 --- a/app/src/main/res/menu/menu_note_activity.xml +++ b/app/src/main/res/menu/menu_note_activity.xml @@ -3,13 +3,13 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_preview" - android:icon="@drawable/ic_eye_white_24dp" + android:icon="@drawable/ic_eye_grey600_24dp" android:orderInCategory="60" android:title="@string/menu_preview" app:showAsAction="ifRoom" /> <item android:id="@+id/menu_edit" - android:icon="@drawable/ic_edit_white_24dp" + android:icon="@drawable/ic_edit_grey600_24dp" android:orderInCategory="60" android:title="@string/simple_edit" app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/menu_note_fragment.xml b/app/src/main/res/menu/menu_note_fragment.xml index 14d8db0e..97cfad95 100644 --- a/app/src/main/res/menu/menu_note_fragment.xml +++ b/app/src/main/res/menu/menu_note_fragment.xml @@ -5,7 +5,7 @@ <item android:id="@+id/search" android:title="@string/action_search" - android:icon="@drawable/ic_search_white_24dp" + android:icon="@drawable/ic_search_grey600_24dp" android:orderInCategory="50" app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="ifRoom|collapseActionView" /> @@ -36,7 +36,7 @@ app:showAsAction="never" /> <item android:id="@+id/menu_move" - android:icon="@drawable/ic_send_white_24dp" + android:icon="@drawable/ic_send_grey600_24dp" android:orderInCategory="120" android:title="@string/simple_move" app:showAsAction="never" /> @@ -48,7 +48,7 @@ app:showAsAction="never" /> <item android:id="@+id/menu_delete" - android:icon="@drawable/ic_delete_white_24dp" + android:icon="@drawable/ic_delete_grey600_24dp" android:orderInCategory="140" android:title="@string/menu_delete" app:showAsAction="never" /> diff --git a/app/src/main/res/values-night/booleans.xml b/app/src/main/res/values-night/booleans.xml new file mode 100644 index 00000000..41cc6feb --- /dev/null +++ b/app/src/main/res/values-night/booleans.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <bool name="isDayMode">false</bool> +</resources> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index c01010ad..3c3e134c 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -2,10 +2,14 @@ <resources> <!-- Colors --> - <color name="primary_dark">#286090</color> + <color name="primary">#121212</color> + <color name="accent">#f5f5f5</color> + + <color name="cursorHandles">#eee5e5ee</color> + <color name="defaultTextHighlightBackground">#55eeeeff</color> <color name="bg_highlighted">#2a2a2a</color> - <color name="bg_normal">#121212</color> + <color name="bg_normal">@color/primary</color> <color name="bg_attention">#ff3333</color> <color name="fg_default">#eeeeee</color> <color name="fg_default_selection">#cccccc</color> @@ -13,5 +17,5 @@ <color name="fg_default_high">#757575</color> <color name="fg_contrast">#000000</color> - <color name="category_background">@color/primary</color> + <color name="category_background">@color/defaultBrand</color> </resources>
\ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 808e2899..1ba46f37 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -151,6 +151,8 @@ <string name="added_content">\"%1$s\" toegevoegd</string> <string name="shared_text_empty">Gedeelde tekst was leeg</string> <string name="append_to_note">Achteraan toevoegen aan notitie</string> + <string name="share_multiple">Deel de inhoud van %1$d notities</string> + <!-- Array: note modes --> <string-array name="noteMode_entries"> <item>Open in wbewerkmodus</item> diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3d6a8234..9c95df31 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -150,6 +150,8 @@ <string name="added_content">\"%1$s\" eklendi</string> <string name="shared_text_empty">Paylaşılan metin boş</string> <string name="append_to_note">Nota ekle</string> + <string name="share_multiple">%1$d notun içeriğini paylaş</string> + <!-- Array: note modes --> <string-array name="noteMode_entries"> <item>Düzenleme kipinde aç</item> diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml new file mode 100644 index 00000000..812e48ee --- /dev/null +++ b/app/src/main/res/values-v23/styles.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <style name="AppTheme" parent="BaseTheme"> + <item name="android:statusBarColor">?attr/colorPrimary</item> + <item name="android:windowLightStatusBar">@bool/isDayMode</item> + </style> + + <style name="SplashTheme" parent="AppTheme"> + <item name="android:windowBackground">@drawable/splashscreen</item> + <item name="android:statusBarColor">@color/defaultBrand</item> + <item name="android:windowLightStatusBar">false</item> + </style> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/values-v27/styles.xml b/app/src/main/res/values-v27/styles.xml new file mode 100644 index 00000000..477789c3 --- /dev/null +++ b/app/src/main/res/values-v27/styles.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <style name="AppTheme" parent="BaseTheme"> + <item name="android:statusBarColor">?attr/colorPrimary</item> + <item name="android:windowLightStatusBar">@bool/isDayMode</item> + <item name="android:navigationBarColor">?attr/colorPrimary</item> + <item name="android:windowLightNavigationBar">@bool/isDayMode</item> + </style> + + <style name="SplashTheme" parent="AppTheme"> + <item name="android:windowBackground">@drawable/splashscreen</item> + <item name="android:statusBarColor">@color/defaultBrand</item> + <item name="android:windowLightStatusBar">false</item> + <item name="android:navigationBarColor">@color/defaultBrand</item> + <item name="android:windowLightNavigationBar">false</item> + </style> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/values/booleans.xml b/app/src/main/res/values/booleans.xml new file mode 100644 index 00000000..c47017c7 --- /dev/null +++ b/app/src/main/res/values/booleans.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <bool name="isDayMode">true</bool> +</resources> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index db265c9f..c9c94fde 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,11 +2,16 @@ <resources> <!-- Colors --> - <color name="primary">#0082C9</color> - <color name="primary_dark">#286090</color> + <color name="primary">#ffffff</color> + <color name="accent">#121212</color> + + <color name="defaultBrand">#0082C9</color> + + <color name="cursorHandles">#555566</color> + <color name="defaultTextHighlightBackground">#2233334a</color> <color name="bg_highlighted">#eee</color> - <color name="bg_normal">#ffffff</color> + <color name="bg_normal">@color/primary</color> <color name="bg_attention">#d40000</color> <color name="bg_warning">#ffcc00</color> <color name="fg_default">#000000</color> @@ -17,7 +22,7 @@ <color name="icon_color_default">#757575</color> - <color name="bg_search_primary">@color/primary</color> + <color name="bg_search_primary">@color/defaultBrand</color> <color name="bg_search_secondary">#eee</color> <color name="widget_background">#dfffffff</color> @@ -25,7 +30,7 @@ <color name="widget_fg_contrast">#ffffff</color> <color name="category_background">@color/bg_normal</color> - <color name="category_border">@color/primary</color> + <color name="category_border">@color/defaultBrand</color> <!-- Dark Theme --> <!-- Defined here until appwidgets can use night/colors --> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index b615b358..d5578788 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -5,6 +5,11 @@ <dimen name="spacer_1hx">4dp</dimen> <dimen name="spacer_1x">8dp</dimen> <dimen name="spacer_2x">16dp</dimen> + <dimen name="spacer_3x">24dp</dimen> + + <!-- Drawer header --> + <dimen name="drawer_header_height">100dp</dimen> + <dimen name="drawer_header_logo_size">42dp</dimen> <!-- Buttons --> <dimen name="button_padding">@dimen/spacer_2x</dimen> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d381d65..7b316949 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ <string name="simple_edit">Edit</string> <string name="action_edit_save">Save</string> <string name="simple_about">About</string> + <string name="simple_accounts">Accounts</string> <string name="simple_bold">Bold</string> <string name="simple_link">Link</string> <string name="simple_italic">Italic</string> @@ -28,6 +29,9 @@ <string name="menu_preview">Preview</string> <string name="menu_share">Share</string> + <string name="search_in_category">Search in %1$s</string> + <string name="search_in_all">Search all notes</string> + <string name="change_category_title">Choose a category</string> <string name="listview_updated_today">Today</string> @@ -112,9 +116,11 @@ <string name="pref_key_note_mode" translatable="false">noteMode</string> <string name="pref_key_theme" translatable="false">darkTheme</string> <string name="pref_key_font" translatable="false">font</string> + <string name="pref_key_branding" translatable="false">branding</string> <string name="pref_key_font_size" translatable="false">fontSize</string> <string name="pref_key_wifi_only" translatable="false">wifiOnly</string> <string name="pref_key_lock" translatable="false">lock</string> + <string name="pref_category_security" translatable="false">security</string> <string name="pref_key_last_note_mode" translatable="false">lastNoteMode</string> <string name="pref_key_background_sync" translatable="false">backgroundSync</string> <string name="pref_value_mode_edit" translatable="false">edit</string> @@ -186,7 +192,13 @@ <string name="append_to_note">Append to note</string> <string name="change_note_title">Change note title</string> <string name="menu_edit_title">Edit title</string> + <string name="settings_branding">Branding</string> + <string name="simple_security">Security</string> + <string name="simple_appearance">Appearance</string> + <string name="simple_synchronization">Synchronization</string> + <string name="simple_behavior">Behavior</string> <string name="share_multiple">Share content of %1$d notes</string> + <string name="manage_accounts">Manage accounts</string> <!-- Array: note modes --> <string-array name="noteMode_entries"> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index edf7acee..ffce1eed 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,16 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="AppTheme" parent="@style/Theme.AppCompat.DayNight.NoActionBar"> + <style name="BaseTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge"> <item name="colorPrimary">@color/primary</item> - <item name="colorPrimaryDark">@color/primary</item> - <item name="colorAccent">@color/primary</item> + <item name="colorAccent">@color/accent</item> + <item name="android:actionModeBackground">?attr/colorPrimary</item> + <item name="colorControlNormal">?attr/colorAccent</item> <item name="windowActionModeOverlay">true</item> - <item name="android:colorBackground">@color/bg_normal</item> </style> - <style name="toolbarStyle" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> - <item name="colorAccent">#fff</item> + <style name="AppTheme" parent="BaseTheme" /> + + <style name="textViewStyle"> + <item name="colorAccent">@color/cursorHandles</item> + <item name="android:textColorHighlight">@color/defaultTextHighlightBackground</item> </style> <style name="fab"> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index c0d1d5ac..3fc5b4f9 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,66 +1,93 @@ <?xml version="1.0" encoding="utf-8"?> -<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <it.niedermann.owncloud.notes.branding.BrandedPreferenceCategory - <SwitchPreference - android:defaultValue="@string/pref_value_lock" - android:icon="@drawable/ic_lock_grey600_24dp" - android:key="@string/pref_key_lock" - android:layout="@layout/item_pref" - android:summary="@string/simple_beta" - android:title="@string/settings_lock" /> + app:layout="@layout/item_preference_category" + app:title="@string/simple_synchronization"> - <ListPreference - android:defaultValue="@string/pref_value_mode_edit" - android:entries="@array/noteMode_entries" - android:entryValues="@array/noteMode_values" - android:icon="@drawable/ic_remove_red_eye_grey_24dp" - android:key="@string/pref_key_note_mode" - android:layout="@layout/item_pref" - android:summary="%s" - android:title="@string/settings_note_mode" /> + <it.niedermann.owncloud.notes.branding.BrandedSwitchPreference + android:defaultValue="@string/pref_value_wifi_and_mobile" + android:icon="@drawable/ic_network_wifi_grey600_24dp" + android:key="@string/pref_key_wifi_only" + android:layout="@layout/item_pref" + android:title="@string/settings_wifi_only" /> - <ListPreference - android:defaultValue="@string/pref_value_theme_system_default" - android:entries="@array/darkmode_entries" - android:entryValues="@array/darkMode_values" - android:summary="%s" - android:icon="@drawable/ic_brightness_2_grey_24dp" - android:key="@string/pref_key_theme" - android:layout="@layout/item_pref" - android:title="@string/settings_theme_title" /> + <ListPreference + android:defaultValue="@string/pref_value_sync_off" + android:entries="@array/sync_entries" + android:entryValues="@array/sync_values" + android:icon="@drawable/ic_sync_black_24dp" + android:key="@string/pref_key_background_sync" + android:layout="@layout/item_pref" + android:summary="%s" + android:title="@string/settings_background_sync" /> + </it.niedermann.owncloud.notes.branding.BrandedPreferenceCategory> - <SwitchPreference - android:defaultValue="@string/pref_value_font_normal" - android:icon="@drawable/ic_text_format_grey600_24dp" - android:key="@string/pref_key_font" - android:layout="@layout/item_pref" - android:title="@string/settings_font_title" /> + <it.niedermann.owncloud.notes.branding.BrandedPreferenceCategory - <ListPreference - android:defaultValue="@string/pref_value_font_size_medium" - android:entries="@array/fontSize_entries" - android:entryValues="@array/fontSize_values" - android:icon="@drawable/ic_format_size_black_24dp" - android:key="@string/pref_key_font_size" - android:layout="@layout/item_pref" - android:summary="%s" - android:title="@string/settings_font_size" /> + app:layout="@layout/item_preference_category" + app:title="@string/simple_appearance"> - <SwitchPreference - android:defaultValue="@string/pref_value_wifi_and_mobile" - android:icon="@drawable/ic_network_wifi_grey600_24dp" - android:key="@string/pref_key_wifi_only" - android:layout="@layout/item_pref" - android:title="@string/settings_wifi_only" /> + <ListPreference + android:defaultValue="@string/pref_value_theme_system_default" + android:entries="@array/darkmode_entries" + android:entryValues="@array/darkMode_values" + android:icon="@drawable/ic_brightness_2_grey_24dp" + android:key="@string/pref_key_theme" + android:layout="@layout/item_pref" + android:summary="%s" + android:title="@string/settings_theme_title" /> - <ListPreference - android:defaultValue="@string/pref_value_sync_off" - android:entries="@array/sync_entries" - android:entryValues="@array/sync_values" - android:icon="@drawable/ic_sync_black_24dp" - android:key="@string/pref_key_background_sync" - android:layout="@layout/item_pref" - android:summary="%s" - android:title="@string/settings_background_sync" /> + <it.niedermann.owncloud.notes.branding.BrandedSwitchPreference + android:defaultValue="true" + android:icon="@drawable/ic_color_lens_grey600_24dp" + android:key="@string/pref_key_branding" + android:layout="@layout/item_pref" + android:title="@string/settings_branding" /> + </it.niedermann.owncloud.notes.branding.BrandedPreferenceCategory> + <it.niedermann.owncloud.notes.branding.BrandedPreferenceCategory + android:key="@string/pref_category_security" + app:layout="@layout/item_preference_category" + app:title="@string/simple_security"> + <it.niedermann.owncloud.notes.branding.BrandedSwitchPreference + android:defaultValue="@string/pref_value_lock" + android:icon="@drawable/ic_lock_grey600_24dp" + android:key="@string/pref_key_lock" + android:layout="@layout/item_pref" + android:summary="@string/simple_beta" + android:title="@string/settings_lock" /> + </it.niedermann.owncloud.notes.branding.BrandedPreferenceCategory> + + <it.niedermann.owncloud.notes.branding.BrandedPreferenceCategory + + app:layout="@layout/item_preference_category" + app:title="@string/simple_behavior"> + <it.niedermann.owncloud.notes.branding.BrandedSwitchPreference + android:defaultValue="@string/pref_value_font_normal" + android:icon="@drawable/ic_text_format_grey600_24dp" + android:key="@string/pref_key_font" + android:layout="@layout/item_pref" + android:title="@string/settings_font_title" /> + <ListPreference + android:defaultValue="@string/pref_value_mode_edit" + android:entries="@array/noteMode_entries" + android:entryValues="@array/noteMode_values" + android:icon="@drawable/ic_remove_red_eye_grey_24dp" + android:key="@string/pref_key_note_mode" + android:layout="@layout/item_pref" + android:summary="%s" + android:title="@string/settings_note_mode" /> + + <ListPreference + android:defaultValue="@string/pref_value_font_size_medium" + android:entries="@array/fontSize_entries" + android:entryValues="@array/fontSize_values" + android:icon="@drawable/ic_format_size_black_24dp" + android:key="@string/pref_key_font_size" + android:layout="@layout/item_pref" + android:summary="%s" + android:title="@string/settings_font_size" /> + </it.niedermann.owncloud.notes.branding.BrandedPreferenceCategory> </PreferenceScreen> |