1 package com.android.documentsui.sorting;
2 
3 import static com.android.documentsui.base.SharedMinimal.TAG;
4 
5 import android.app.Dialog;
6 import android.content.Context;
7 import android.os.Bundle;
8 import android.util.Log;
9 import android.view.View;
10 import android.view.ViewGroup;
11 import android.widget.AdapterView;
12 import android.widget.ArrayAdapter;
13 import android.widget.CheckedTextView;
14 import android.widget.FrameLayout;
15 import android.widget.ListView;
16 
17 import androidx.annotation.StringRes;
18 import androidx.fragment.app.DialogFragment;
19 import androidx.fragment.app.FragmentManager;
20 
21 import com.android.documentsui.R;
22 import com.android.documentsui.sorting.SortDimension.SortDirection;
23 
24 import com.google.android.material.bottomsheet.BottomSheetBehavior;
25 import com.google.android.material.bottomsheet.BottomSheetDialog;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 
30 public class SortListFragment extends DialogFragment {
31 
32     private static final String TAG_MODEL = "sorting_model";
33     private static final String TAG_SORTING_LIST = "sorting_list";
34 
35     private SortModel mModel;
36     private List<SortItem> mSortingList;
37 
show(FragmentManager fm, SortModel model)38     public static void show(FragmentManager fm, SortModel model) {
39         if (fm.isStateSaved()) {
40             Log.w(TAG, "Skip show sort dialog because state saved");
41             return;
42         }
43 
44         if (fm.findFragmentByTag(TAG_SORTING_LIST) == null) {
45             SortListFragment fragment = new SortListFragment();
46             Bundle args = new Bundle();
47             args.putParcelable(TAG_MODEL, model);
48             fragment.setArguments(args);
49             fragment.show(fm, TAG_SORTING_LIST);
50         }
51     }
52 
SortListFragment()53     public SortListFragment() {
54         super();
55     }
56 
onItemClicked(AdapterView<?> parent, View view, int position, long id)57     private void onItemClicked (AdapterView<?> parent, View view, int position, long id) {
58         SortItem item = mSortingList.get(position);
59         mModel.sortByUser(item.id, item.direction);
60         getDialog().dismiss();
61     }
62 
setupSortingList()63     private void setupSortingList() {
64         mSortingList = new ArrayList<>();
65         for (int i = 0; i < mModel.getSize(); ++i) {
66             SortDimension dimension = mModel.getDimensionAt(i);
67             if (dimension.getSortCapability() != SortDimension.SORT_CAPABILITY_NONE) {
68                 final int id = dimension.getId();
69                 if (id == SortModel.SORT_DIMENSION_ID_TITLE
70                         || id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
71                     addBothDirectionDimension(dimension, true);
72                 } else if (id == SortModel.SORT_DIMENSION_ID_DATE
73                         || id == SortModel.SORT_DIMENSION_ID_SIZE) {
74                     addBothDirectionDimension(dimension, false);
75                 } else {
76                     mSortingList.add(new SortItem(dimension));
77                 }
78             }
79         }
80     }
81 
addBothDirectionDimension(SortDimension source, boolean ascendingFirst)82     private void addBothDirectionDimension(SortDimension source, boolean ascendingFirst) {
83         SortItem ascending = new SortItem(source.getId(),
84                 SortDimension.SORT_DIRECTION_ASCENDING,
85                 getSheetLabelId(source, SortDimension.SORT_DIRECTION_ASCENDING));
86         SortItem descending = new SortItem(source.getId(),
87                 SortDimension.SORT_DIRECTION_DESCENDING,
88                 getSheetLabelId(source, SortDimension.SORT_DIRECTION_DESCENDING));
89         mSortingList.add(ascendingFirst ? ascending : descending);
90         mSortingList.add(ascendingFirst ? descending : ascending);
91     }
92 
getSheetLabelId(SortDimension dimension, @SortDirection int direction)93     public static @StringRes int getSheetLabelId(SortDimension dimension, @SortDirection int direction) {
94         boolean isAscending = direction == SortDimension.SORT_DIRECTION_ASCENDING;
95         final int id = dimension.getId();
96         if (id == SortModel.SORT_DIMENSION_ID_TITLE) {
97             return isAscending ? R.string.sort_dimension_name_ascending :
98                     R.string.sort_dimension_name_descending;
99         } else if (id == SortModel.SORT_DIMENSION_ID_DATE) {
100             return isAscending ? R.string.sort_dimension_date_ascending :
101                     R.string.sort_dimension_date_descending;
102         } else if (id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
103             return isAscending ? R.string.sort_dimension_file_type_ascending :
104                     R.string.sort_dimension_file_type_descending;
105         } else if (id == SortModel.SORT_DIMENSION_ID_SIZE) {
106             return isAscending ? R.string.sort_dimension_size_ascending :
107                     R.string.sort_dimension_size_descending;
108         }
109         return dimension.getLabelId();
110     }
111 
112     @Override
onCreateDialog(Bundle savedInstanceState)113     public Dialog onCreateDialog(Bundle savedInstanceState) {
114         if (savedInstanceState == null) {
115             Bundle args = getArguments();
116             mModel = args.getParcelable(TAG_MODEL);
117         } else {
118             mModel = savedInstanceState.getParcelable(TAG_MODEL);
119         }
120         setupSortingList();
121 
122         BottomSheetDialog dialog =
123                 new BottomSheetDialog(getContext());
124         dialog.setContentView(R.layout.dialog_sorting);
125 
126         // Workaround for solve issue about dialog not full expanded when landscape.
127         FrameLayout bottomSheet = (FrameLayout)
128                 dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
129         BottomSheetBehavior.from(bottomSheet)
130                 .setState(BottomSheetBehavior.STATE_EXPANDED);
131 
132         ListView listView = dialog.findViewById(R.id.sorting_dialog_list);
133 
134         listView.setAdapter(new SortingListAdapter(getContext(), mSortingList));
135         listView.setOnItemClickListener(this::onItemClicked);
136 
137         return dialog;
138     }
139 
140     @Override
onSaveInstanceState(Bundle outState)141     public void onSaveInstanceState(Bundle outState) {
142         super.onSaveInstanceState(outState);
143         outState.putParcelable(TAG_MODEL, mModel);
144     }
145 
146     private class SortingListAdapter extends ArrayAdapter<SortItem> {
147 
SortingListAdapter(Context context, List<SortItem> list)148         public SortingListAdapter(Context context, List<SortItem> list) {
149             super(context, R.layout.sort_list_item, list);
150         }
151 
152         @Override
getView(int position, View convertView, ViewGroup parent)153         public View getView(int position, View convertView, ViewGroup parent) {
154             View view = super.getView(position, convertView, parent);
155             final SortItem item = getItem(position);
156             final CheckedTextView text = view.findViewById(android.R.id.text1);
157             text.setText(getString(item.labelId));
158 
159             boolean selected = item.id == mModel.getSortedDimensionId()
160                     && item.direction == mModel.getCurrentSortDirection();
161             text.setChecked(selected);
162             return view;
163         }
164     }
165 
166     private static class SortItem {
167 
168         final int id;
169         @SortDirection final int direction;
170         @StringRes final int labelId;
171 
SortItem(SortDimension dimension)172         SortItem(SortDimension dimension) {
173             id = dimension.getId();
174             direction = dimension.getDefaultSortDirection();
175             labelId = dimension.getLabelId();
176         }
177 
SortItem(int id, @SortDirection int direction, @StringRes int labelId)178         SortItem(int id, @SortDirection int direction, @StringRes int labelId) {
179             this.id = id;
180             this.direction = direction;
181             this.labelId = labelId;
182         }
183     }
184 }
185