Browse Source

feat: implement refresh messages list

main
Distopico Vegan 6 years ago
parent
commit
f5973df777
11 changed files with 1245 additions and 119 deletions
  1. +1046
    -0
      app/schemas/org.dystopia.email.DB/25.json
  2. +12
    -3
      app/src/main/java/org/dystopia/email/DB.java
  3. +45
    -84
      app/src/main/java/org/dystopia/email/DaoFolder.java
  4. +4
    -2
      app/src/main/java/org/dystopia/email/DaoOperation.java
  5. +2
    -0
      app/src/main/java/org/dystopia/email/EntityFolder.java
  6. +22
    -7
      app/src/main/java/org/dystopia/email/EntityOperation.java
  7. +90
    -10
      app/src/main/java/org/dystopia/email/FragmentMessages.java
  8. +6
    -6
      app/src/main/java/org/dystopia/email/ServiceSynchronize.java
  9. +4
    -0
      app/src/main/java/org/dystopia/email/TupleFolderEx.java
  10. +13
    -7
      app/src/main/res/layout/fragment_messages.xml
  11. +1
    -0
      app/src/main/res/values/dimens.xml

+ 1046
- 0
app/schemas/org.dystopia.email.DB/25.json
File diff suppressed because it is too large
View File


+ 12
- 3
app/src/main/java/org/dystopia/email/DB.java View File

@ -41,7 +41,7 @@ import org.json.JSONObject;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 24,
version = 25,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -349,11 +349,20 @@ public abstract class DB extends RoomDatabase {
@Override
public void migrate(SupportSQLiteDatabase db) {
Log.i(
Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion);
Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `message` ADD COLUMN `account_name` TEXT");
}
})
.build();
.addMigrations(
new Migration(24, 25) {
@Override
public void migrate(SupportSQLiteDatabase db) {
Log.i(
Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `folder` ADD COLUMN `sync_state` TEXT");
}
})
.build();
}
public static class Converters {


+ 45
- 84
app/src/main/java/org/dystopia/email/DaoFolder.java View File

@ -17,6 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import androidx.lifecycle.LiveData;
@ -27,76 +28,53 @@ import java.util.List;
@Dao
public interface DaoFolder {
@Query(
"SELECT * FROM folder"
+ " WHERE account = :account"
+ " ORDER BY CASE WHEN folder.type = '"
+ EntityFolder.USER
+ "' THEN 1 ELSE 0 END")
@Query("SELECT * FROM folder" + " WHERE account = :account"
+ " ORDER BY CASE WHEN folder.type = '" + EntityFolder.USER + "' THEN 1 ELSE 0 END")
List<EntityFolder> getFolders(long account);
@Query(
"SELECT * FROM folder"
+ " WHERE account = :account"
+ " AND synchronize = :synchronize"
+ " ORDER BY CASE WHEN folder.type = '"
+ EntityFolder.USER
+ "' THEN 1 ELSE 0 END")
@Query("SELECT * FROM folder" + " WHERE account = :account" + " AND synchronize = :synchronize"
+ " ORDER BY CASE WHEN folder.type = '" + EntityFolder.USER + "' THEN 1 ELSE 0 END")
List<EntityFolder> getFolders(long account, boolean synchronize);
@Query(
"SELECT * FROM folder"
+ " WHERE account = :account"
+ " AND type = '"
+ EntityFolder.USER
+ "'")
@Query("SELECT * FROM folder" + " WHERE account = :account" + " AND type = '" + EntityFolder.USER
+ "'")
List<EntityFolder> getUserFolders(long account);
@Query(
"SELECT folder.*, account.name AS accountName"
+ ", COUNT(message.id) AS messages"
+ ", SUM(CASE WHEN message.content = 1 THEN 1 ELSE 0 END) AS content"
+ ", SUM(CASE WHEN message.ui_seen = 0 THEN 1 ELSE 0 END) AS unseen"
+ " FROM folder"
+ " LEFT JOIN account ON account.id = folder.account"
+ " LEFT JOIN message ON message.folder = folder.id AND NOT message.ui_hide"
+ " WHERE folder.account = :account OR folder.account IS NULL"
+ " GROUP BY folder.id")
@Query("SELECT * FROM folder WHERE unified")
List<EntityFolder> getUnifiedFolders();
@Query("SELECT folder.*, account.name AS accountName, account.state as accountState"
+ ", COUNT(message.id) AS messages"
+ ", SUM(CASE WHEN message.content = 1 THEN 1 ELSE 0 END) AS content"
+ ", SUM(CASE WHEN message.ui_seen = 0 THEN 1 ELSE 0 END) AS unseen" + " FROM folder"
+ " LEFT JOIN account ON account.id = folder.account"
+ " LEFT JOIN message ON message.folder = folder.id AND NOT message.ui_hide"
+ " WHERE folder.account = :account OR folder.account IS NULL" + " GROUP BY folder.id")
LiveData<List<TupleFolderEx>> liveFolders(long account);
@Query(
"SELECT * FROM folder"
+ " WHERE (:account < 0 OR folder.account = :account)"
+ " AND type <> '"
+ EntityFolder.USER
+ "'")
@Query("SELECT * FROM folder" + " WHERE (:account < 0 OR folder.account = :account)"
+ " AND type <> '" + EntityFolder.USER + "'")
LiveData<List<EntityFolder>> liveSystemFolders(long account);
@Query(
"SELECT folder.*, account.name AS accountName"
+ ", COUNT(message.id) AS messages"
+ ", SUM(CASE WHEN message.content = 1 THEN 1 ELSE 0 END) AS content"
+ ", SUM(CASE WHEN message.ui_seen = 0 THEN 1 ELSE 0 END) AS unseen"
+ " FROM folder"
+ " JOIN account ON account.id = folder.account"
+ " JOIN message ON message.folder = folder.id AND NOT message.ui_hide"
+ " WHERE account.`synchronize`"
+ " AND folder.unified"
+ " GROUP BY folder.id")
@Query("SELECT folder.*, account.name AS accountName, account.state as accountState"
+ ", COUNT(message.id) AS messages"
+ ", SUM(CASE WHEN message.content = 1 THEN 1 ELSE 0 END) AS content"
+ ", SUM(CASE WHEN message.ui_seen = 0 THEN 1 ELSE 0 END) AS unseen" + " FROM folder"
+ " JOIN account ON account.id = folder.account"
+ " JOIN message ON message.folder = folder.id AND NOT message.ui_hide"
+ " WHERE account.`synchronize`" + " AND folder.unified" + " GROUP BY folder.id")
LiveData<List<TupleFolderEx>> liveUnified();
@Query("SELECT folder.* FROM folder WHERE folder.id = :id")
LiveData<EntityFolder> liveFolder(long id);
@Query(
"SELECT folder.*, account.name AS accountName"
+ ", COUNT(message.id) AS messages"
+ ", SUM(CASE WHEN message.content = 1 THEN 1 ELSE 0 END) AS content"
+ ", SUM(CASE WHEN message.ui_seen = 0 THEN 1 ELSE 0 END) AS unseen"
+ " FROM folder"
+ " LEFT JOIN account ON account.id = folder.account"
+ " LEFT JOIN message ON message.folder = folder.id AND NOT message.ui_hide"
+ " WHERE folder.id = :id")
@Query("SELECT folder.*, account.name AS accountName, account.state as accountState"
+ ", COUNT(message.id) AS messages"
+ ", SUM(CASE WHEN message.content = 1 THEN 1 ELSE 0 END) AS content"
+ ", SUM(CASE WHEN message.ui_seen = 0 THEN 1 ELSE 0 END) AS unseen" + " FROM folder"
+ " LEFT JOIN account ON account.id = folder.account"
+ " LEFT JOIN message ON message.folder = folder.id AND NOT message.ui_hide"
+ " WHERE folder.id = :id")
LiveData<TupleFolderEx> liveFolderEx(long id);
@Query("SELECT * FROM folder WHERE id = :id")
@ -109,20 +87,12 @@ public interface DaoFolder {
EntityFolder getFolderByType(long account, String type);
// For debug/crash info
@Query(
"SELECT folder.* FROM folder"
+ " JOIN account ON account.id = folder.account"
+ " WHERE `primary` AND type = '"
+ EntityFolder.DRAFTS
+ "'")
@Query("SELECT folder.* FROM folder" + " JOIN account ON account.id = folder.account"
+ " WHERE `primary` AND type = '" + EntityFolder.DRAFTS + "'")
EntityFolder getPrimaryDrafts();
@Query(
"SELECT folder.* FROM folder"
+ " JOIN account ON account.id = folder.account"
+ " WHERE `primary` AND type = '"
+ EntityFolder.ARCHIVE
+ "'")
@Query("SELECT folder.* FROM folder" + " JOIN account ON account.id = folder.account"
+ " WHERE `primary` AND type = '" + EntityFolder.ARCHIVE + "'")
EntityFolder getPrimaryArchive();
@Query("SELECT * FROM folder WHERE type = '" + EntityFolder.OUTBOX + "'")
@ -134,6 +104,9 @@ public interface DaoFolder {
@Query("UPDATE folder SET state = :state WHERE id = :id")
int setFolderState(long id, String state);
@Query("UPDATE folder SET sync_state = :state WHERE id = :id")
int setFolderSyncState(long id, String state);
@Query("UPDATE folder SET error = :error WHERE id = :id")
int setFolderError(long id, String error);
@ -143,23 +116,11 @@ public interface DaoFolder {
@Query("UPDATE folder" + " SET type = '" + EntityFolder.USER + "'" + " WHERE account = :account")
int setFoldersUser(long account);
@Query(
"UPDATE folder"
+ " SET name = :name"
+ ", display = :display"
+ ", hide = :hide"
+ ", synchronize = :synchronize"
+ ", unified = :unified"
+ ", `after` = :after"
+ " WHERE id = :id")
int setFolderProperties(
long id,
String name,
String display,
boolean hide,
boolean synchronize,
boolean unified,
int after);
@Query("UPDATE folder" + " SET name = :name" + ", display = :display" + ", hide = :hide"
+ ", synchronize = :synchronize" + ", unified = :unified" + ", `after` = :after"
+ " WHERE id = :id")
int setFolderProperties(long id, String name, String display, boolean hide, boolean synchronize,
boolean unified, int after);
@Query("UPDATE folder SET name = :name WHERE account = :account AND name = :old")
int renameFolder(long account, String old, String name);


+ 4
- 2
app/src/main/java/org/dystopia/email/DaoOperation.java View File

@ -36,8 +36,10 @@ public interface DaoOperation {
@Query("SELECT * FROM operation ORDER BY id")
LiveData<List<EntityOperation>> liveOperations();
@Query("SELECT COUNT(id) FROM operation WHERE folder = :folder")
int getOperationCount(long folder);
@Query("SELECT COUNT(id) FROM operation" +
" WHERE folder = :folder" +
" AND (:name IS NULL OR operation.name = :name)")
int getOperationCount(long folder, String name);
@Insert
long insertOperation(EntityOperation operation);


+ 2
- 0
app/src/main/java/org/dystopia/email/EntityFolder.java View File

@ -66,6 +66,7 @@ public class EntityFolder implements Serializable {
@NonNull public Boolean hide = false;
@NonNull public Boolean unified = false;
public String state;
public String sync_state;
public String error;
static final String INBOX = "Inbox";
@ -112,6 +113,7 @@ public class EntityFolder implements Serializable {
&& this.hide == other.hide
&& this.unified == other.unified
&& (this.state == null ? other.state == null : this.state.equals(other.state))
&& (this.sync_state == null ? other.sync_state == null : this.sync_state.equals(other.sync_state))
&& (this.error == null ? other.error == null : this.error.equals(other.error)));
} else {
return false;


+ 22
- 7
app/src/main/java/org/dystopia/email/EntityOperation.java View File

@ -71,6 +71,7 @@ public class EntityOperation {
public static final String BODY = "body";
public static final String ATTACHMENT = "attachment";
public static final String FLAG = "flag";
public static final String SYNC = "sync";
private static List<Intent> queue = new ArrayList<>();
@ -85,13 +86,6 @@ public class EntityOperation {
queue(db, message, name, jsonArray);
}
static void queue(DB db, EntityMessage message, String name, Object value1, Object value2) {
JSONArray jsonArray = new JSONArray();
jsonArray.put(value1);
jsonArray.put(value2);
queue(db, message, name, jsonArray);
}
private static void queue(DB db, EntityMessage message, String name, JSONArray jsonArray) {
EntityOperation operation = new EntityOperation();
operation.folder = message.folder;
@ -124,6 +118,20 @@ public class EntityOperation {
+ operation.args);
}
private static void queue(DB db, long folder, Long message, String name, JSONArray jargs) {
EntityOperation operation = new EntityOperation();
operation.folder = folder;
operation.message = message;
operation.name = name;
operation.args = jargs.toString();
operation.created = new Date().getTime();
operation.id = db.operation().insertOperation(operation);
Log.i(Helper.TAG, "Queued op=" + operation.id + "/" + operation.name +
" msg=" + operation.folder + "/" + operation.message +
" args=" + operation.args);
}
public static void process(Context context) {
// Processing needs to be done after committing to the database
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
@ -135,6 +143,13 @@ public class EntityOperation {
}
}
static void sync(DB db, long folder) {
if (db.operation().getOperationCount(folder, EntityOperation.SYNC) == 0) {
queue(db, folder, null, EntityOperation.SYNC, new JSONArray());
db.folder().setFolderSyncState(folder, "requested");
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof EntityOperation) {


+ 90
- 10
app/src/main/java/org/dystopia/email/FragmentMessages.java View File

@ -2,18 +2,18 @@ package org.dystopia.email;
/*
* This file is part of FairEmail.
*
*
* FairEmail is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
*
* FairEmail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License along with FairEmail. If not,
* see <http://www.gnu.org/licenses/>.
*
*
* Copyright 2018, Marcel Bokhorst (M66B)
* Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
@ -59,6 +59,7 @@ import androidx.recyclerview.selection.StorageStrategy;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
@ -73,6 +74,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FragmentMessages extends FragmentEx {
private SwipeRefreshLayout swipeRefresh;
private ViewGroup view;
private View popupAnchor;
private ImageButton ibHintSupport;
@ -157,6 +159,7 @@ public class FragmentMessages extends FragmentEx {
setHasOptionsMenu(true);
// Get controls
swipeRefresh = view.findViewById(R.id.swipeRefresh);
popupAnchor = view.findViewById(R.id.popupAnchor);
ibHintSupport = view.findViewById(R.id.ibHintSupport);
ibHintSwipe = view.findViewById(R.id.ibHintSwipe);
@ -176,6 +179,16 @@ public class FragmentMessages extends FragmentEx {
// Wire controls
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Bundle args = new Bundle();
args.putLong("account", account);
args.putLong("folder", folder);
onRefreshHandler(args);
}
});
ibHintSwipe.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -275,10 +288,13 @@ public class FragmentMessages extends FragmentEx {
selectionTracker.addObserver(new SelectionTracker.SelectionObserver() {
@Override
public void onSelectionChanged() {
swipeRefresh.setEnabled(false);
if (selectionTracker.hasSelection()) {
fabMove.show();
} else {
fabMove.hide();
swipeRefresh.setEnabled(true);
}
}
});
@ -718,10 +734,12 @@ public class FragmentMessages extends FragmentEx {
});
// Initialize
swipeRefresh.setEnabled(viewType == AdapterMessage.ViewType.UNIFIED || viewType == AdapterMessage.ViewType.FOLDER);
tvNoEmail.setVisibility(View.GONE);
bottom_navigation.setVisibility(View.GONE);
grpReady.setVisibility(View.GONE);
pbWait.setVisibility(View.VISIBLE);
// TODO: implement in load more items
pbWait.setVisibility(View.GONE);
fab.hide();
fabMove.hide();
@ -803,6 +821,16 @@ public class FragmentMessages extends FragmentEx {
} else {
setSubtitle(name);
}
boolean isRefreshing = false;
for (TupleFolderEx folder : folders) {
if (folder.sync_state != null && "connected".equals(folder.accountState)) {
isRefreshing = true;
break;
}
}
swipeRefresh.setRefreshing(isRefreshing);
}
});
break;
@ -827,6 +855,10 @@ public class FragmentMessages extends FragmentEx {
outbox = EntityFolder.OUTBOX.equals(folder.type);
getActivity().invalidateOptionsMenu();
}
swipeRefresh.setRefreshing(folder != null && folder.sync_state != null &&
"connected".equals(EntityFolder.OUTBOX.equals(folder.type)
? folder.state : folder.accountState));
}
});
break;
@ -1014,6 +1046,54 @@ public class FragmentMessages extends FragmentEx {
}
}
private void onRefreshHandler(Bundle args) {
new SimpleTask<Boolean>() {
@Override
protected Boolean onLoad(Context context, Bundle args) {
long aid = args.getLong("account");
long fid = args.getLong("folder");
DB db = DB.getInstance(context);
boolean isConnected = false;
try {
db.beginTransaction();
List<EntityFolder> folders = new ArrayList<>();
if (aid < 0) {
folders.addAll(db.folder().getUnifiedFolders());
} else {
folders.add(db.folder().getFolder(fid));
}
for (EntityFolder folder : folders) {
EntityOperation.sync(db, folder.id);
if (folder.account == null) { // outbox
isConnected = "connected".equals(folder.state);
} else {
EntityAccount account = db.account().getAccount(folder.account);
isConnected = "connected".equals(account.state);
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return isConnected;
}
@Override
protected void onLoaded(Bundle args, Boolean isConnected) {
if (!isConnected) {
swipeRefresh.setRefreshing(false);
}
}
}.load(FragmentMessages.this, args);
}
private void onMenuFolders() {
getFragmentManager().popBackStack("unified", 0);
@ -1098,12 +1178,12 @@ public class FragmentMessages extends FragmentEx {
new BoundaryCallbackMessages.IBoundaryCallbackMessages() {
@Override
public void onLoading() {
pbWait.setVisibility(View.VISIBLE);
swipeRefresh.setRefreshing(true);
}
@Override
public void onLoaded() {
pbWait.setVisibility(View.GONE);
swipeRefresh.setRefreshing(false);
}
@Override
@ -1141,12 +1221,12 @@ public class FragmentMessages extends FragmentEx {
@Override
public void onLoading() {
tvNoEmail.setVisibility(View.GONE);
pbWait.setVisibility(View.VISIBLE);
swipeRefresh.setRefreshing(true);
}
@Override
public void onLoaded() {
pbWait.setVisibility(View.GONE);
swipeRefresh.setRefreshing(false);
if (messages.getValue() == null || messages.getValue().size() == 0) {
tvNoEmail.setVisibility(View.VISIBLE);
}
@ -1247,7 +1327,7 @@ public class FragmentMessages extends FragmentEx {
boolean searching = (searchCallback != null && searchCallback.isSearching());
if (!searching) {
pbWait.setVisibility(View.GONE);
swipeRefresh.setRefreshing(false);
}
grpReady.setVisibility(View.VISIBLE);


+ 6
- 6
app/src/main/java/org/dystopia/email/ServiceSynchronize.java View File

@ -2,18 +2,18 @@ package org.dystopia.email;
/*
* This file is part of FairEmail.
*
*
* FairEmail is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
*
* FairEmail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License along with FairEmail. If not,
* see <http://www.gnu.org/licenses/>.
*
*
* Copyright 2018, Marcel Bokhorst (M66B)
* Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
@ -1093,7 +1093,7 @@ public class ServiceSynchronize extends LifecycleService {
// Prevent unnecessary folder
// connections
if (ACTION_PROCESS_OPERATIONS.equals(intent.getAction())) {
if (db.operation().getOperationCount(fid) == 0) {
if (db.operation().getOperationCount(fid, null) == 0) {
return;
}
}
@ -1152,7 +1152,7 @@ public class ServiceSynchronize extends LifecycleService {
lbm.registerReceiver(processFolder, f);
for (EntityFolder folder : folders.keySet()) {
if (db.operation().getOperationCount(folder.id) > 0) {
if (db.operation().getOperationCount(folder.id, null) > 0) {
Intent intent = new Intent();
intent.setType("account/" + account.id);
intent.setAction(ServiceSynchronize.ACTION_PROCESS_OPERATIONS);


+ 4
- 0
app/src/main/java/org/dystopia/email/TupleFolderEx.java View File

@ -21,6 +21,7 @@ package org.dystopia.email;
public class TupleFolderEx extends EntityFolder {
public String accountName;
public String accountState;
public int messages;
public int content;
public int unseen;
@ -33,6 +34,9 @@ public class TupleFolderEx extends EntityFolder {
&& (this.accountName == null
? other.accountName == null
: accountName.equals(other.accountName))
&& (this.accountState == null
? other.accountState == null
: accountState.equals(other.accountState))
&& this.messages == other.messages
&& this.content == other.content
&& this.unseen == other.unseen);


+ 13
- 7
app/src/main/res/layout/fragment_messages.xml View File

@ -6,6 +6,11 @@
android:layout_height="match_parent"
tools:context=".ActivityView">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -29,7 +34,7 @@
android:textColor="?android:attr/textColorPrimary"
app:layout_constraintEnd_toStartOf="@+id/ibHintSupport"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
@ -136,13 +141,12 @@
<ProgressBar
android:id="@+id/pbWait"
style="@style/Base.Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="@dimen/normal_size"
android:layout_height="@dimen/normal_size"
android:indeterminate="true"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/vSeparatorHintSelect" />
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
@ -180,7 +184,9 @@
android:layout_width="0dp"
android:layout_height="0dp"
app:constraint_referenced_ids="rvFolder" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabMove"


+ 1
- 0
app/src/main/res/values/dimens.xml View File

@ -14,5 +14,6 @@
<dimen name="content_margin">8dp</dimen>
<dimen name="compose_padding">6dp</dimen>
<dimen name="input_height">40dp</dimen>
<dimen name="normal_size">40dp</dimen>
<dimen name="color_pick">24dp</dimen>
</resources>

Loading…
Cancel
Save