Collapsible navigation drawer groups

pull/996/head
Alex Baker 4 years ago
parent a946b8e216
commit fba0f1cfec

File diff suppressed because it is too large Load Diff

@ -17,14 +17,19 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.LocalBroadcastManager;
import org.tasks.billing.Inventory; import org.tasks.billing.Inventory;
import org.tasks.data.CaldavDao;
import org.tasks.data.GoogleTaskDao;
import org.tasks.filters.NavigationDrawerSubheader; import org.tasks.filters.NavigationDrawerSubheader;
import org.tasks.locale.Locale; import org.tasks.locale.Locale;
import org.tasks.preferences.Preferences;
import org.tasks.themes.ColorProvider; import org.tasks.themes.ColorProvider;
import org.tasks.themes.Theme; import org.tasks.themes.Theme;
import org.tasks.themes.ThemeAccent; import org.tasks.themes.ThemeAccent;
@ -39,6 +44,10 @@ public class FilterAdapter extends BaseAdapter {
private final Locale locale; private final Locale locale;
private final Inventory inventory; private final Inventory inventory;
private final ColorProvider colorProvider; private final ColorProvider colorProvider;
private final Preferences preferences;
private final GoogleTaskDao googleTaskDao;
private final CaldavDao caldavDao;
private final LocalBroadcastManager localBroadcastManager;
private final LayoutInflater inflater; private final LayoutInflater inflater;
private Filter selected = null; private Filter selected = null;
private List<FilterListItem> items = new ArrayList<>(); private List<FilterListItem> items = new ArrayList<>();
@ -49,12 +58,20 @@ public class FilterAdapter extends BaseAdapter {
Theme theme, Theme theme,
Locale locale, Locale locale,
Inventory inventory, Inventory inventory,
ColorProvider colorProvider) { ColorProvider colorProvider,
Preferences preferences,
GoogleTaskDao googleTaskDao,
CaldavDao caldavDao,
LocalBroadcastManager localBroadcastManager) {
this.activity = activity; this.activity = activity;
this.accent = theme.getThemeAccent(); this.accent = theme.getThemeAccent();
this.locale = locale; this.locale = locale;
this.inventory = inventory; this.inventory = inventory;
this.colorProvider = colorProvider; this.colorProvider = colorProvider;
this.preferences = preferences;
this.googleTaskDao = googleTaskDao;
this.caldavDao = caldavDao;
this.localBroadcastManager = localBroadcastManager;
this.inflater = theme.getLayoutInflater(activity); this.inflater = theme.getLayoutInflater(activity);
} }
@ -69,13 +86,9 @@ public class FilterAdapter extends BaseAdapter {
} }
public void setData(List<FilterListItem> items, @Nullable Filter selected) { public void setData(List<FilterListItem> items, @Nullable Filter selected) {
setData(items, selected, -1);
}
public void setData(List<FilterListItem> items, @Nullable Filter selected, int defaultIndex) {
assertMainThread(); assertMainThread();
this.items = items; this.items = items;
this.selected = defaultIndex >= 0 ? getFilter(indexOf(selected, defaultIndex)) : selected; this.selected = selected;
notifyDataSetChanged(); notifyDataSetChanged();
} }
@ -91,11 +104,6 @@ public class FilterAdapter extends BaseAdapter {
return items.get(position); return items.get(position);
} }
private Filter getFilter(int position) {
FilterListItem item = getItem(position);
return item instanceof Filter ? (Filter) item : null;
}
@Override @Override
public long getItemId(int position) { public long getItemId(int position) {
return position; return position;
@ -105,7 +113,7 @@ public class FilterAdapter extends BaseAdapter {
private View newView(View convertView, ViewGroup parent, FilterListItem.Type viewType) { private View newView(View convertView, ViewGroup parent, FilterListItem.Type viewType) {
if (convertView == null) { if (convertView == null) {
convertView = inflater.inflate(viewType.layout, parent, false); convertView = inflater.inflate(viewType.layout, parent, false);
FilterViewHolder viewHolder; ViewHolder viewHolder;
switch (viewType) { switch (viewType) {
case ITEM: case ITEM:
viewHolder = viewHolder =
@ -116,7 +124,14 @@ public class FilterAdapter extends BaseAdapter {
viewHolder = new FilterViewHolder(convertView); viewHolder = new FilterViewHolder(convertView);
break; break;
case SUBHEADER: case SUBHEADER:
viewHolder = new FilterViewHolder(convertView, activity); viewHolder =
new SubheaderViewHolder(
convertView,
activity,
preferences,
googleTaskDao,
caldavDao,
localBroadcastManager);
break; break;
default: default:
throw new RuntimeException(); throw new RuntimeException();
@ -142,13 +157,13 @@ public class FilterAdapter extends BaseAdapter {
public View getView(int position, View convertView, @NonNull ViewGroup parent) { public View getView(int position, View convertView, @NonNull ViewGroup parent) {
FilterListItem item = getItem(position); FilterListItem item = getItem(position);
convertView = newView(convertView, parent, item.getItemType()); convertView = newView(convertView, parent, item.getItemType());
FilterViewHolder viewHolder = (FilterViewHolder) convertView.getTag(); ViewHolder viewHolder = (ViewHolder) convertView.getTag();
switch (item.getItemType()) { switch (item.getItemType()) {
case ITEM: case ITEM:
viewHolder.bind(item, item.equals(selected), 0); ((FilterViewHolder) viewHolder).bind(item, item.equals(selected), 0);
break; break;
case SUBHEADER: case SUBHEADER:
viewHolder.bind((NavigationDrawerSubheader) item); ((SubheaderViewHolder) viewHolder).bind((NavigationDrawerSubheader) item);
break; break;
case SEPARATOR: case SEPARATOR:
break; break;

@ -25,7 +25,6 @@ import com.todoroo.astrid.api.GtasksFilter;
import com.todoroo.astrid.api.TagFilter; import com.todoroo.astrid.api.TagFilter;
import org.tasks.R; import org.tasks.R;
import org.tasks.billing.Inventory; import org.tasks.billing.Inventory;
import org.tasks.filters.NavigationDrawerSubheader;
import org.tasks.locale.Locale; import org.tasks.locale.Locale;
import org.tasks.preferences.SyncPreferences; import org.tasks.preferences.SyncPreferences;
import org.tasks.themes.ColorProvider; import org.tasks.themes.ColorProvider;
@ -164,11 +163,6 @@ public class FilterViewHolder extends RecyclerView.ViewHolder {
} }
} }
public void bind(NavigationDrawerSubheader filter) {
text.setText(filter.listingTitle);
icon.setVisibility(filter.error ? View.VISIBLE : View.GONE);
}
public interface OnClick { public interface OnClick {
void onClick(@Nullable FilterListItem item); void onClick(@Nullable FilterListItem item);
} }

@ -20,6 +20,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil.ItemCallback; import androidx.recyclerview.widget.DiffUtil.ItemCallback;
import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.todoroo.astrid.adapter.FilterViewHolder.OnClick; import com.todoroo.astrid.adapter.FilterViewHolder.OnClick;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
@ -27,14 +28,18 @@ import com.todoroo.astrid.api.FilterListItem.Type;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.LocalBroadcastManager;
import org.tasks.billing.Inventory; import org.tasks.billing.Inventory;
import org.tasks.data.CaldavDao;
import org.tasks.data.GoogleTaskDao;
import org.tasks.filters.NavigationDrawerSubheader; import org.tasks.filters.NavigationDrawerSubheader;
import org.tasks.locale.Locale; import org.tasks.locale.Locale;
import org.tasks.preferences.Preferences;
import org.tasks.themes.ColorProvider; import org.tasks.themes.ColorProvider;
import org.tasks.themes.Theme; import org.tasks.themes.Theme;
import org.tasks.themes.ThemeAccent; import org.tasks.themes.ThemeAccent;
public class NavigationDrawerAdapter extends ListAdapter<FilterListItem, FilterViewHolder> { public class NavigationDrawerAdapter extends ListAdapter<FilterListItem, ViewHolder> {
private static final String TOKEN_SELECTED = "token_selected"; private static final String TOKEN_SELECTED = "token_selected";
private final Activity activity; private final Activity activity;
@ -42,6 +47,10 @@ public class NavigationDrawerAdapter extends ListAdapter<FilterListItem, FilterV
private final Locale locale; private final Locale locale;
private final Inventory inventory; private final Inventory inventory;
private final ColorProvider colorProvider; private final ColorProvider colorProvider;
private final Preferences preferences;
private final GoogleTaskDao googleTaskDao;
private final CaldavDao caldavDao;
private final LocalBroadcastManager localBroadcastManager;
private final LayoutInflater inflater; private final LayoutInflater inflater;
private OnClick onClick; private OnClick onClick;
private Filter selected = null; private Filter selected = null;
@ -53,13 +62,21 @@ public class NavigationDrawerAdapter extends ListAdapter<FilterListItem, FilterV
Theme theme, Theme theme,
Locale locale, Locale locale,
Inventory inventory, Inventory inventory,
ColorProvider colorProvider) { ColorProvider colorProvider,
Preferences preferences,
GoogleTaskDao googleTaskDao,
CaldavDao caldavDao,
LocalBroadcastManager localBroadcastManager) {
super(new DiffCallback()); super(new DiffCallback());
this.activity = activity; this.activity = activity;
this.accent = theme.getThemeAccent(); this.accent = theme.getThemeAccent();
this.locale = locale; this.locale = locale;
this.inventory = inventory; this.inventory = inventory;
this.colorProvider = colorProvider; this.colorProvider = colorProvider;
this.preferences = preferences;
this.googleTaskDao = googleTaskDao;
this.caldavDao = caldavDao;
this.localBroadcastManager = localBroadcastManager;
this.inflater = theme.getLayoutInflater(activity); this.inflater = theme.getLayoutInflater(activity);
} }
@ -97,14 +114,15 @@ public class NavigationDrawerAdapter extends ListAdapter<FilterListItem, FilterV
@NonNull @NonNull
@Override @Override
public FilterViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Type type = Type.values()[viewType]; Type type = Type.values()[viewType];
View view = inflater.inflate(type.layout, parent, false); View view = inflater.inflate(type.layout, parent, false);
if (type == ITEM) { if (type == ITEM) {
return new FilterViewHolder( return new FilterViewHolder(
view, accent, true, locale, activity, inventory, colorProvider, this::onClickFilter); view, accent, true, locale, activity, inventory, colorProvider, this::onClickFilter);
} else if (type == SUBHEADER) { } else if (type == SUBHEADER) {
return new FilterViewHolder(view, activity); return new SubheaderViewHolder(
view, activity, preferences, googleTaskDao, caldavDao, localBroadcastManager);
} else { } else {
return new FilterViewHolder(view); return new FilterViewHolder(view);
} }
@ -115,13 +133,14 @@ public class NavigationDrawerAdapter extends ListAdapter<FilterListItem, FilterV
} }
@Override @Override
public void onBindViewHolder(@NonNull FilterViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
FilterListItem item = getItem(position); FilterListItem item = getItem(position);
Type type = item.getItemType(); Type type = item.getItemType();
if (type == ITEM) { if (type == ITEM) {
holder.bind(item, item.equals(selected), item.count >= 0 ? item.count : counts.get(item)); ((FilterViewHolder) holder)
.bind(item, item.equals(selected), item.count >= 0 ? item.count : counts.get(item));
} else if (type == SUBHEADER) { } else if (type == SUBHEADER) {
holder.bind((NavigationDrawerSubheader) item); ((SubheaderViewHolder) holder).bind((NavigationDrawerSubheader) item);
} }
} }

@ -0,0 +1,83 @@
package com.todoroo.astrid.adapter;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.data.CaldavDao;
import org.tasks.data.GoogleTaskDao;
import org.tasks.filters.NavigationDrawerSubheader;
import org.tasks.preferences.Preferences;
import org.tasks.preferences.SyncPreferences;
import org.tasks.themes.DrawableUtil;
public class SubheaderViewHolder extends RecyclerView.ViewHolder {
private final Preferences preferences;
private final GoogleTaskDao googleTaskDao;
private final CaldavDao caldavDao;
private final LocalBroadcastManager localBroadcastManager;
@BindView(R.id.text)
TextView text;
@BindView(R.id.icon_error)
ImageView errorIcon;
private NavigationDrawerSubheader subheader;
SubheaderViewHolder(
@NonNull View itemView,
Activity activity,
Preferences preferences,
GoogleTaskDao googleTaskDao,
CaldavDao caldavDao,
LocalBroadcastManager localBroadcastManager) {
super(itemView);
this.preferences = preferences;
this.googleTaskDao = googleTaskDao;
this.caldavDao = caldavDao;
this.localBroadcastManager = localBroadcastManager;
ButterKnife.bind(this, itemView);
errorIcon.setOnClickListener(
v -> activity.startActivity(new Intent(activity, SyncPreferences.class)));
}
@OnClick(R.id.subheader_row)
public void onClick() {
boolean collapsed = !subheader.isCollapsed();
switch (subheader.getSubheaderType()) {
case PREFERENCE:
preferences.setBoolean((int) subheader.getId(), collapsed);
break;
case GOOGLE_TASKS:
googleTaskDao.setCollapsed(subheader.getId(), collapsed);
break;
case CALDAV:
caldavDao.setCollapsed(subheader.getId(), collapsed);
break;
}
localBroadcastManager.broadcastRefreshList();
}
public void bind(NavigationDrawerSubheader subheader) {
this.subheader = subheader;
text.setText(subheader.listingTitle);
errorIcon.setVisibility(subheader.error ? View.VISIBLE : View.GONE);
DrawableUtil.setRightDrawable(
itemView.getContext(),
text,
subheader.isCollapsed()
? R.drawable.ic_keyboard_arrow_up_black_18dp
: R.drawable.ic_keyboard_arrow_down_black_18dp);
}
}

@ -58,7 +58,7 @@ import org.tasks.notifications.NotificationDao;
CaldavAccount.class, CaldavAccount.class,
GoogleTaskAccount.class GoogleTaskAccount.class
}, },
version = 71) version = 72)
public abstract class Database extends RoomDatabase { public abstract class Database extends RoomDatabase {
public static final String NAME = "database"; public static final String NAME = "database";

@ -0,0 +1,5 @@
package org.tasks;
public interface Function<T> {
T call();
}

@ -1,5 +1,7 @@
package org.tasks.activities; package org.tasks.activities;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
@ -10,6 +12,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.LocalBroadcastManager;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
import org.tasks.filters.FilterProvider; import org.tasks.filters.FilterProvider;
import org.tasks.injection.ActivityComponent; import org.tasks.injection.ActivityComponent;
@ -26,9 +29,16 @@ public class FilterSelectionActivity extends InjectingAppCompatActivity {
@Inject DialogBuilder dialogBuilder; @Inject DialogBuilder dialogBuilder;
@Inject FilterAdapter filterAdapter; @Inject FilterAdapter filterAdapter;
@Inject FilterProvider filterProvider; @Inject FilterProvider filterProvider;
@Inject LocalBroadcastManager localBroadcastManager;
private CompositeDisposable disposables; private CompositeDisposable disposables;
private Filter selected; private Filter selected;
private BroadcastReceiver refreshReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
refresh();
}
};
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -72,17 +82,18 @@ public class FilterSelectionActivity extends InjectingAppCompatActivity {
super.onResume(); super.onResume();
disposables = new CompositeDisposable(); disposables = new CompositeDisposable();
disposables.add(
Single.fromCallable(() -> filterProvider.getItems(false)) localBroadcastManager.registerRefreshListReceiver(refreshReceiver);
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) refresh();
.subscribe(items -> filterAdapter.setData(items, selected)));
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
localBroadcastManager.unregisterReceiver(refreshReceiver);
disposables.dispose(); disposables.dispose();
} }
@ -93,6 +104,14 @@ public class FilterSelectionActivity extends InjectingAppCompatActivity {
filterAdapter.save(outState); filterAdapter.save(outState);
} }
private void refresh() {
disposables.add(
Single.fromCallable(() -> filterProvider.getItems(false))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> filterAdapter.setData(items, selected)));
}
@Override @Override
public void inject(ActivityComponent component) { public void inject(ActivityComponent component) {
component.inject(this); component.inject(this);

@ -2,6 +2,8 @@ package org.tasks.activities;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -17,6 +19,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.LocalBroadcastManager;
import org.tasks.R; import org.tasks.R;
import org.tasks.dialogs.AlertDialogBuilder; import org.tasks.dialogs.AlertDialogBuilder;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
@ -37,8 +40,15 @@ public class RemoteListPicker extends InjectingDialogFragment
@Inject FilterAdapter filterAdapter; @Inject FilterAdapter filterAdapter;
@Inject FilterProvider filterProvider; @Inject FilterProvider filterProvider;
@Inject SyncAdapters syncAdapters; @Inject SyncAdapters syncAdapters;
@Inject LocalBroadcastManager localBroadcastManager;
private CompositeDisposable disposables; private CompositeDisposable disposables;
private BroadcastReceiver refreshReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
refresh();
}
};
public static RemoteListPicker newRemoteListSupportPicker( public static RemoteListPicker newRemoteListSupportPicker(
Filter selected, Fragment targetFragment, int requestCode) { Filter selected, Fragment targetFragment, int requestCode) {
@ -103,22 +113,19 @@ public class RemoteListPicker extends InjectingDialogFragment
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
Bundle arguments = getArguments(); disposables = new CompositeDisposable();
boolean noSelection = arguments.getBoolean(EXTRA_NO_SELECTION, false);
Filter selected = noSelection ? null : arguments.getParcelable(EXTRA_SELECTED_FILTER);
disposables = localBroadcastManager.registerRefreshListReceiver(refreshReceiver);
new CompositeDisposable(
Single.fromCallable(filterProvider::getRemoteListPickerItems) refresh();
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> filterAdapter.setData(items, selected, noSelection ? -1 : 0)));
} }
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
localBroadcastManager.unregisterReceiver(refreshReceiver);
disposables.dispose(); disposables.dispose();
} }
@ -147,4 +154,15 @@ public class RemoteListPicker extends InjectingDialogFragment
Activity.RESULT_OK, Activity.RESULT_OK,
new Intent().putExtra(EXTRA_SELECTED_FILTER, filter)); new Intent().putExtra(EXTRA_SELECTED_FILTER, filter));
} }
private void refresh() {
Bundle arguments = getArguments();
boolean noSelection = arguments.getBoolean(EXTRA_NO_SELECTION, false);
Filter selected = noSelection ? null : arguments.getParcelable(EXTRA_SELECTED_FILTER);
disposables.add(Single.fromCallable(filterProvider::getRemoteListPickerItems)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> filterAdapter.setData(items, selected)));
}
} }

@ -61,6 +61,9 @@ public class CaldavAccount implements Parcelable {
@ColumnInfo(name = "cda_account_type") @ColumnInfo(name = "cda_account_type")
private int accountType; private int accountType;
@ColumnInfo(name = "cda_collapsed")
private boolean collapsed;
public CaldavAccount() {} public CaldavAccount() {}
@Ignore @Ignore
@ -75,6 +78,7 @@ public class CaldavAccount implements Parcelable {
suppressRepeatingTasks = ParcelCompat.readBoolean(source); suppressRepeatingTasks = ParcelCompat.readBoolean(source);
accountType = source.readInt(); accountType = source.readInt();
encryptionKey = source.readString(); encryptionKey = source.readString();
collapsed = ParcelCompat.readBoolean(source);
} }
public long getId() { public long getId() {
@ -173,6 +177,14 @@ public class CaldavAccount implements Parcelable {
return accountType == TYPE_ETESYNC; return accountType == TYPE_ETESYNC;
} }
public boolean isCollapsed() {
return collapsed;
}
public void setCollapsed(boolean collapsed) {
this.collapsed = collapsed;
}
@Override @Override
public String toString() { public String toString() {
return "CaldavAccount{" return "CaldavAccount{"
@ -203,6 +215,8 @@ public class CaldavAccount implements Parcelable {
+ '\'' + '\''
+ ", accountType=" + ", accountType="
+ accountType + accountType
+ ", collapsed="
+ collapsed
+ '}'; + '}';
} }
@ -226,6 +240,9 @@ public class CaldavAccount implements Parcelable {
if (accountType != that.accountType) { if (accountType != that.accountType) {
return false; return false;
} }
if (collapsed != that.collapsed) {
return false;
}
if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) { if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) {
return false; return false;
} }
@ -261,6 +278,7 @@ public class CaldavAccount implements Parcelable {
result = 31 * result + (suppressRepeatingTasks ? 1 : 0); result = 31 * result + (suppressRepeatingTasks ? 1 : 0);
result = 31 * result + (encryptionKey != null ? encryptionKey.hashCode() : 0); result = 31 * result + (encryptionKey != null ? encryptionKey.hashCode() : 0);
result = 31 * result + accountType; result = 31 * result + accountType;
result = 31 * result + (collapsed ? 1 : 0);
return result; return result;
} }
@ -281,5 +299,6 @@ public class CaldavAccount implements Parcelable {
ParcelCompat.writeBoolean(dest, suppressRepeatingTasks); ParcelCompat.writeBoolean(dest, suppressRepeatingTasks);
dest.writeInt(accountType); dest.writeInt(accountType);
dest.writeString(encryptionKey); dest.writeString(encryptionKey);
ParcelCompat.writeBoolean(dest, collapsed);
} }
} }

@ -30,6 +30,9 @@ public abstract class CaldavDao {
@Query("SELECT * FROM caldav_accounts ORDER BY UPPER(cda_name) ASC") @Query("SELECT * FROM caldav_accounts ORDER BY UPPER(cda_name) ASC")
public abstract List<CaldavAccount> getAccounts(); public abstract List<CaldavAccount> getAccounts();
@Query("UPDATE caldav_accounts SET cda_collapsed = :collapsed WHERE cda_id = :id")
public abstract void setCollapsed(long id, boolean collapsed);
@Insert @Insert
public abstract long insert(CaldavAccount caldavAccount); public abstract long insert(CaldavAccount caldavAccount);

@ -2,6 +2,7 @@ package org.tasks.data;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.core.os.ParcelCompat;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
@ -35,6 +36,9 @@ public class GoogleTaskAccount implements Parcelable {
@ColumnInfo(name = "gta_etag") @ColumnInfo(name = "gta_etag")
private String etag; private String etag;
@ColumnInfo(name = "gta_collapsed")
private boolean collapsed;
public GoogleTaskAccount() {} public GoogleTaskAccount() {}
@Ignore @Ignore
@ -43,6 +47,7 @@ public class GoogleTaskAccount implements Parcelable {
account = source.readString(); account = source.readString();
error = source.readString(); error = source.readString();
etag = source.readString(); etag = source.readString();
collapsed = ParcelCompat.readBoolean(source);
} }
@Ignore @Ignore
@ -82,12 +87,20 @@ public class GoogleTaskAccount implements Parcelable {
this.etag = etag; this.etag = etag;
} }
public boolean isCollapsed() {
return collapsed;
}
public void setCollapsed(boolean collapsed) {
this.collapsed = collapsed;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
if (o == null || getClass() != o.getClass()) { if (!(o instanceof GoogleTaskAccount)) {
return false; return false;
} }
@ -96,6 +109,9 @@ public class GoogleTaskAccount implements Parcelable {
if (id != that.id) { if (id != that.id) {
return false; return false;
} }
if (collapsed != that.collapsed) {
return false;
}
if (account != null ? !account.equals(that.account) : that.account != null) { if (account != null ? !account.equals(that.account) : that.account != null) {
return false; return false;
} }
@ -111,6 +127,7 @@ public class GoogleTaskAccount implements Parcelable {
result = 31 * result + (account != null ? account.hashCode() : 0); result = 31 * result + (account != null ? account.hashCode() : 0);
result = 31 * result + (error != null ? error.hashCode() : 0); result = 31 * result + (error != null ? error.hashCode() : 0);
result = 31 * result + (etag != null ? etag.hashCode() : 0); result = 31 * result + (etag != null ? etag.hashCode() : 0);
result = 31 * result + (collapsed ? 1 : 0);
return result; return result;
} }
@ -128,6 +145,8 @@ public class GoogleTaskAccount implements Parcelable {
+ ", etag='" + ", etag='"
+ etag + etag
+ '\'' + '\''
+ ", collapsed="
+ collapsed
+ '}'; + '}';
} }
@ -142,5 +161,6 @@ public class GoogleTaskAccount implements Parcelable {
dest.writeString(account); dest.writeString(account);
dest.writeString(error); dest.writeString(error);
dest.writeString(etag); dest.writeString(etag);
ParcelCompat.writeBoolean(dest, collapsed);
} }
} }

@ -67,6 +67,9 @@ public abstract class GoogleTaskDao {
update(task); update(task);
} }
@Query("UPDATE google_task_accounts SET gta_collapsed = :collapsed WHERE gta_id = :id")
public abstract void setCollapsed(long id, boolean collapsed);
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1") @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1")
public abstract GoogleTask getByTaskId(long taskId); public abstract GoogleTask getByTaskId(long taskId);

@ -391,6 +391,17 @@ public class Migrations {
} }
}; };
private static final Migration MIGRATION_71_72 =
new Migration(71, 72) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL(
"ALTER TABLE `caldav_accounts` ADD COLUMN `cda_collapsed` INTEGER NOT NULL DEFAULT 0");
database.execSQL(
"ALTER TABLE `google_task_accounts` ADD COLUMN `gta_collapsed` INTEGER NOT NULL DEFAULT 0");
}
};
public static final Migration[] MIGRATIONS = public static final Migration[] MIGRATIONS =
new Migration[] { new Migration[] {
MIGRATION_35_36, MIGRATION_35_36,
@ -419,7 +430,8 @@ public class Migrations {
MIGRATION_67_68, MIGRATION_67_68,
MIGRATION_68_69, MIGRATION_68_69,
MIGRATION_69_70, MIGRATION_69_70,
MIGRATION_70_71 MIGRATION_70_71,
MIGRATION_71_72
}; };
private static Migration NOOP(int from, int to) { private static Migration NOOP(int from, int to) {

@ -21,12 +21,14 @@ import com.todoroo.astrid.gtasks.GtasksFilterExposer;
import com.todoroo.astrid.tags.TagFilterExposer; import com.todoroo.astrid.tags.TagFilterExposer;
import com.todoroo.astrid.timers.TimerFilterExposer; import com.todoroo.astrid.timers.TimerFilterExposer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.BuildConfig; import org.tasks.BuildConfig;
import org.tasks.Function;
import org.tasks.R; import org.tasks.R;
import org.tasks.activities.GoogleTaskListSettingsActivity; import org.tasks.activities.GoogleTaskListSettingsActivity;
import org.tasks.activities.TagSettingsActivity; import org.tasks.activities.TagSettingsActivity;
@ -36,9 +38,11 @@ import org.tasks.caldav.CaldavFilterExposer;
import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavAccount;
import org.tasks.data.GoogleTaskAccount; import org.tasks.data.GoogleTaskAccount;
import org.tasks.etesync.EteSyncCalendarSettingsActivity; import org.tasks.etesync.EteSyncCalendarSettingsActivity;
import org.tasks.filters.NavigationDrawerSubheader.SubheaderType;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
import org.tasks.preferences.HelpAndFeedback; import org.tasks.preferences.HelpAndFeedback;
import org.tasks.preferences.MainPreferences; import org.tasks.preferences.MainPreferences;
import org.tasks.preferences.Preferences;
import org.tasks.ui.NavigationDrawerFragment; import org.tasks.ui.NavigationDrawerFragment;
public class FilterProvider { public class FilterProvider {
@ -51,6 +55,7 @@ public class FilterProvider {
private final TagFilterExposer tagFilterExposer; private final TagFilterExposer tagFilterExposer;
private final GtasksFilterExposer gtasksFilterExposer; private final GtasksFilterExposer gtasksFilterExposer;
private final CaldavFilterExposer caldavFilterExposer; private final CaldavFilterExposer caldavFilterExposer;
private final Preferences preferences;
@Inject @Inject
public FilterProvider( public FilterProvider(
@ -61,7 +66,8 @@ public class FilterProvider {
CustomFilterExposer customFilterExposer, CustomFilterExposer customFilterExposer,
TagFilterExposer tagFilterExposer, TagFilterExposer tagFilterExposer,
GtasksFilterExposer gtasksFilterExposer, GtasksFilterExposer gtasksFilterExposer,
CaldavFilterExposer caldavFilterExposer) { CaldavFilterExposer caldavFilterExposer,
Preferences preferences) {
this.context = context; this.context = context;
this.inventory = inventory; this.inventory = inventory;
this.builtInFilterExposer = builtInFilterExposer; this.builtInFilterExposer = builtInFilterExposer;
@ -70,6 +76,7 @@ public class FilterProvider {
this.tagFilterExposer = tagFilterExposer; this.tagFilterExposer = tagFilterExposer;
this.gtasksFilterExposer = gtasksFilterExposer; this.gtasksFilterExposer = gtasksFilterExposer;
this.caldavFilterExposer = caldavFilterExposer; this.caldavFilterExposer = caldavFilterExposer;
this.preferences = preferences;
} }
public List<FilterListItem> getRemoteListPickerItems() { public List<FilterListItem> getRemoteListPickerItems() {
@ -85,14 +92,26 @@ public class FilterProvider {
GoogleTaskAccount account = filters.getKey(); GoogleTaskAccount account = filters.getKey();
items.addAll( items.addAll(
getSubmenu( getSubmenu(
account.getAccount(), !isNullOrEmpty(account.getError()), filters.getValue(), true)); account.getAccount(),
!isNullOrEmpty(account.getError()),
account.isCollapsed() ? Collections.emptyList() : filters.getValue(),
true,
account.isCollapsed(),
SubheaderType.GOOGLE_TASKS,
account.getId()));
} }
for (Map.Entry<CaldavAccount, List<Filter>> filters : getCaldavFilters()) { for (Map.Entry<CaldavAccount, List<Filter>> filters : getCaldavFilters()) {
CaldavAccount account = filters.getKey(); CaldavAccount account = filters.getKey();
items.addAll( items.addAll(
getSubmenu( getSubmenu(
account.getName(), !isNullOrEmpty(account.getError()), filters.getValue(), true)); account.getName(),
!isNullOrEmpty(account.getError()),
account.isCollapsed() ? Collections.emptyList() : filters.getValue(),
true,
account.isCollapsed(),
SubheaderType.CALDAV,
account.getId()));
} }
return items; return items;
@ -105,9 +124,9 @@ public class FilterProvider {
items.add(builtInFilterExposer.getMyTasksFilter()); items.add(builtInFilterExposer.getMyTasksFilter());
items.addAll(getSubmenu(R.string.filters, getFilters())); items.addAll(getSubmenu(R.string.filters, R.string.p_collapse_filters, this::getFilters));
if (navigationDrawer) { if (navigationDrawer && !preferences.getBoolean(R.string.p_collapse_filters, false)) {
items.add( items.add(
new NavigationDrawerAction( new NavigationDrawerAction(
context.getString(R.string.FLA_new_filter), context.getString(R.string.FLA_new_filter),
@ -116,9 +135,10 @@ public class FilterProvider {
NavigationDrawerFragment.REQUEST_NEW_LIST)); NavigationDrawerFragment.REQUEST_NEW_LIST));
} }
items.addAll(getSubmenu(R.string.tags, tagFilterExposer.getFilters())); items.addAll(
getSubmenu(R.string.tags, R.string.p_collapse_tags, tagFilterExposer::getFilters));
if (navigationDrawer) { if (navigationDrawer && !preferences.getBoolean(R.string.p_collapse_tags, false)) {
items.add( items.add(
new NavigationDrawerAction( new NavigationDrawerAction(
context.getString(R.string.new_tag), context.getString(R.string.new_tag),
@ -133,10 +153,13 @@ public class FilterProvider {
getSubmenu( getSubmenu(
account.getAccount(), account.getAccount(),
!isNullOrEmpty(account.getError()), !isNullOrEmpty(account.getError()),
filters.getValue(), account.isCollapsed() ? Collections.emptyList() : filters.getValue(),
!navigationDrawer)); !navigationDrawer,
account.isCollapsed(),
SubheaderType.GOOGLE_TASKS,
account.getId()));
if (navigationDrawer) { if (navigationDrawer && !account.isCollapsed()) {
items.add( items.add(
new NavigationDrawerAction( new NavigationDrawerAction(
context.getString(R.string.new_list), context.getString(R.string.new_list),
@ -153,10 +176,13 @@ public class FilterProvider {
getSubmenu( getSubmenu(
account.getName(), account.getName(),
!isNullOrEmpty(account.getError()), !isNullOrEmpty(account.getError()),
filters.getValue(), account.isCollapsed() ? Collections.emptyList() : filters.getValue(),
!navigationDrawer)); !navigationDrawer,
account.isCollapsed(),
SubheaderType.CALDAV,
account.getId()));
if (navigationDrawer) { if (navigationDrawer && !account.isCollapsed()) {
items.add( items.add(
new NavigationDrawerAction( new NavigationDrawerAction(
context.getString(R.string.new_list), context.getString(R.string.new_list),
@ -223,15 +249,29 @@ public class FilterProvider {
return caldavFilterExposer.getFilters().entrySet(); return caldavFilterExposer.getFilters().entrySet();
} }
private List<FilterListItem> getSubmenu(int title, List<Filter> filters) { private List<FilterListItem> getSubmenu(int title, int prefId, Function<List<Filter>> getFilters) {
return getSubmenu(context.getString(title), false, filters, false); boolean collapsed = preferences.getBoolean(prefId, false);
return newArrayList(
concat(
ImmutableList.of(
new NavigationDrawerSubheader(
context.getString(title), false, collapsed, SubheaderType.PREFERENCE, prefId)),
collapsed ? Collections.emptyList() : getFilters.call()));
} }
private List<FilterListItem> getSubmenu( private List<FilterListItem> getSubmenu(
String title, boolean error, List<Filter> filters, boolean hideIfEmpty) { String title,
return hideIfEmpty && filters.isEmpty() boolean error,
List<Filter> filters,
boolean hideIfEmpty,
boolean collapsed,
SubheaderType type,
long id) {
return hideIfEmpty && filters.isEmpty() && !collapsed
? ImmutableList.of() ? ImmutableList.of()
: newArrayList( : newArrayList(
concat(ImmutableList.of(new NavigationDrawerSubheader(title, error)), filters)); concat(
ImmutableList.of(new NavigationDrawerSubheader(title, error, collapsed, type, id)),
filters));
} }
} }

@ -3,6 +3,7 @@ package org.tasks.filters;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.os.ParcelCompat;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
public class NavigationDrawerSubheader extends FilterListItem { public class NavigationDrawerSubheader extends FilterListItem {
@ -24,40 +25,104 @@ public class NavigationDrawerSubheader extends FilterListItem {
return new NavigationDrawerSubheader[size]; return new NavigationDrawerSubheader[size];
} }
}; };
public boolean error; public boolean error;
private boolean collapsed;
private SubheaderType subheaderType;
private long id;
private NavigationDrawerSubheader() {} private NavigationDrawerSubheader() {}
public NavigationDrawerSubheader(String listingTitle, boolean error) { public NavigationDrawerSubheader(
String listingTitle, boolean error, boolean collapsed, SubheaderType subheaderType, long id) {
this.error = error; this.error = error;
this.collapsed = collapsed;
this.subheaderType = subheaderType;
this.id = id;
this.listingTitle = listingTitle; this.listingTitle = listingTitle;
} }
public long getId() {
return id;
}
public boolean isCollapsed() {
return collapsed;
}
public SubheaderType getSubheaderType() {
return subheaderType;
}
@Override @Override
protected void readFromParcel(Parcel source) { protected void readFromParcel(Parcel source) {
super.readFromParcel(source); super.readFromParcel(source);
error = source.readInt() == 1; error = ParcelCompat.readBoolean(source);
collapsed = ParcelCompat.readBoolean(source);
subheaderType = (SubheaderType) source.readSerializable();
id = source.readLong();
} }
@Override @Override
public boolean areItemsTheSame(@NonNull FilterListItem other) { public boolean areItemsTheSame(@NonNull FilterListItem other) {
return other instanceof NavigationDrawerSubheader && listingTitle.equals(other.listingTitle); return other instanceof NavigationDrawerSubheader
&& subheaderType == ((NavigationDrawerSubheader) other).getSubheaderType()
&& id == ((NavigationDrawerSubheader) other).getId();
} }
@Override @Override
public boolean areContentsTheSame(@NonNull FilterListItem other) { public boolean areContentsTheSame(@NonNull FilterListItem other) {
return super.areContentsTheSame(other) && error == ((NavigationDrawerSubheader) other).error; return this.equals(other);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof NavigationDrawerSubheader)) {
return false;
}
NavigationDrawerSubheader that = (NavigationDrawerSubheader) o;
if (error != that.error) {
return false;
}
if (collapsed != that.collapsed) {
return false;
}
if (id != that.id) {
return false;
}
return subheaderType == that.subheaderType;
}
@Override
public int hashCode() {
int result = (error ? 1 : 0);
result = 31 * result + (collapsed ? 1 : 0);
result = 31 * result + (subheaderType != null ? subheaderType.hashCode() : 0);
result = 31 * result + (int) (id ^ (id >>> 32));
return result;
} }
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags); super.writeToParcel(dest, flags);
dest.writeInt(error ? 1 : 0); ParcelCompat.writeBoolean(dest, error);
ParcelCompat.writeBoolean(dest, collapsed);
dest.writeSerializable(subheaderType);
dest.writeLong(id);
} }
@Override @Override
public Type getItemType() { public Type getItemType() {
return Type.SUBHEADER; return Type.SUBHEADER;
} }
public enum SubheaderType {
PREFERENCE,
GOOGLE_TASKS,
CALDAV
}
} }

@ -25,6 +25,15 @@ public class DrawableUtil {
} }
} }
public static void setRightDrawable(Context context, TextView tv, @DrawableRes int resId) {
Drawable wrapped = getWrapped(context, resId);
if (atLeastJellybeanMR1()) {
tv.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, wrapped, null);
} else {
tv.setCompoundDrawablesWithIntrinsicBounds(null, null, wrapped, null);
}
}
public static Drawable getLeftDrawable(TextView tv) { public static Drawable getLeftDrawable(TextView tv) {
return atLeastJellybeanMR1() return atLeastJellybeanMR1()
? tv.getCompoundDrawablesRelative()[0] ? tv.getCompoundDrawablesRelative()[0]

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="18dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="18dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
</vector>

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/row" android:id="@+id/row"
android:background="@drawable/drawer_background_selector" android:background="@drawable/drawer_background_selector"
android:foreground="?attr/selectableItemBackground"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="12dp" android:paddingTop="12dp"

@ -2,12 +2,14 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:background="@drawable/drawer_background_selector" android:background="@drawable/drawer_background_selector"
android:focusable="false" android:focusable="false"
android:orientation="vertical"> android:orientation="vertical">
<View style="@style/horizontal_divider"/> <View
android:id="@+id/divider"
android:paddingBottom="4dp"
style="@style/horizontal_divider"
android:layout_gravity="top" />
</LinearLayout> </LinearLayout>

@ -2,58 +2,62 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:clickable="true"
android:paddingTop="8dp" android:id="@+id/subheader_row"
android:background="@drawable/drawer_background_selector" android:background="?attr/selectableItemBackground"
android:focusable="true"
android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<View <View
android:id="@+id/divider" android:id="@+id/divider"
style="@style/horizontal_divider" android:paddingBottom="4dp"
android:layout_gravity="top"/> style="@style/horizontal_divider"
android:layout_gravity="top" />
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon_error"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_below="@id/divider" android:layout_alignBottom="@id/text"
android:paddingTop="12dp" android:layout_below="@id/divider"
android:paddingStart="0dp" android:paddingTop="8dp"
android:paddingEnd="@dimen/keyline_first" android:paddingBottom="8dp"
android:paddingLeft="0dp" android:paddingStart="0dp"
android:paddingRight="@dimen/keyline_first" android:paddingEnd="@dimen/keyline_first"
android:alpha="@dimen/alpha_secondary" android:paddingLeft="0dp"
android:clickable="true" android:paddingRight="@dimen/keyline_first"
android:focusable="true" android:alpha="@dimen/alpha_secondary"
android:scaleType="center" android:clickable="true"
android:src="@drawable/ic_outline_sync_problem_24px" android:focusable="true"
android:tint="@color/icon_tint" android:scaleType="center"
android:visibility="gone"/> android:src="@drawable/ic_outline_sync_problem_24px"
android:tint="@color/overdue"
android:visibility="gone" />
<CheckedTextView <TextView
android:id="@+id/text" android:id="@+id/text"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:ellipsize="end"
android:layout_alignParentStart="true" android:textColor="@color/text_secondary"
android:layout_below="@id/divider" android:fontFamily="@string/font_fontFamily_medium"
android:layout_toLeftOf="@id/icon" android:layout_alignParentLeft="true"
android:layout_toStartOf="@id/icon" android:layout_alignParentStart="true"
android:paddingTop="12dp" android:layout_below="@id/divider"
android:paddingStart="@dimen/keyline_first" android:paddingTop="16dp"
android:paddingEnd="@dimen/keyline_first" android:paddingBottom="16dp"
android:paddingLeft="@dimen/keyline_first" android:gravity="start|center_vertical"
android:paddingRight="@dimen/keyline_first" android:drawableTint="@color/icon_tint_with_alpha"
android:alpha="0.54" android:singleLine="true"
android:clickable="false" android:textAlignment="viewStart"
android:ellipsize="end" android:textSize="14sp"
android:fontFamily="@string/font_fontFamily_medium" android:paddingStart="@dimen/keyline_first"
android:gravity="start|center_vertical" android:paddingLeft="@dimen/keyline_first"
android:singleLine="true" android:paddingEnd="@dimen/keyline_first"
android:textAlignment="viewStart" android:paddingRight="@dimen/keyline_first"
android:textSize="14sp" tools:ignore="UnusedAttribute" />
tools:ignore="UnusedAttribute"/>
</RelativeLayout> </RelativeLayout>

@ -291,4 +291,6 @@
<string name="p_chip_style">chip_style</string> <string name="p_chip_style">chip_style</string>
<string name="p_chip_appearance">chip_appearance</string> <string name="p_chip_appearance">chip_appearance</string>
<string name="p_desaturate_colors">desaturate_colors</string> <string name="p_desaturate_colors">desaturate_colors</string>
<string name="p_collapse_filters">collapse_filters</string>
<string name="p_collapse_tags">collapse_tags</string>
</resources> </resources>

Loading…
Cancel
Save