1- /************************************************************************************
1+ /*
22 * This file is part of AndroidIDE.
33 *
4- *
5- *
64 * AndroidIDE is free software: you can redistribute it and/or modify
75 * it under the terms of the GNU General Public License as published by
86 * the Free Software Foundation, either version 3 of the License, or
1614 * You should have received a copy of the GNU General Public License
1715 * along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
1816 *
19- ************************************************************************************** /
17+ */
2018package com .itsaky .androidide .fragments ;
2119
20+ import static com .unnamed .b .atv .view .AndroidTreeView .NODES_PATH_SEPARATOR ;
21+
2222import android .content .Context ;
2323import android .os .Bundle ;
24+ import android .text .TextUtils ;
2425import android .view .LayoutInflater ;
2526import android .view .View ;
2627import android .view .ViewGroup ;
4344import com .itsaky .androidide .tasks .TaskExecutor ;
4445import com .itsaky .androidide .tasks .callables .FileTreeCallable ;
4546import com .itsaky .androidide .utils .Environment ;
47+ import com .itsaky .androidide .utils .Logger ;
4648import com .itsaky .androidide .views .editor .CodeEditorView ;
4749import com .itsaky .toaster .Toaster ;
4850import com .unnamed .b .atv .model .TreeNode ;
4951import com .unnamed .b .atv .view .AndroidTreeView ;
5052
5153import java .io .File ;
5254import java .util .Arrays ;
55+ import java .util .HashSet ;
56+ import java .util .Set ;
5357
5458public class FileTreeFragment extends BottomSheetDialogFragment
5559 implements TreeNode .TreeNodeClickListener , TreeNode .TreeNodeLongClickListener {
5660
61+ private static final String KEY_STORED_TREE_STATE = "fileTree_state" ;
62+ private static final Logger LOG = Logger .newInstance ("FileTreeFragment" );
5763 private LayoutEditorFileTreeBinding binding ;
5864 private AndroidTreeView mFileTreeView ;
5965 private FileActionListener mFileActionListener ;
6066 private AndroidProject mProject ;
6167 private TreeNode mRoot ;
68+ private String mTreeState ;
6269
6370 public FileTreeFragment () {}
6471
@@ -71,6 +78,52 @@ public static FileTreeFragment newInstance(AndroidProject project) {
7178 return frag ;
7279 }
7380
81+ public void saveTreeState () {
82+ if (mFileTreeView != null ) {
83+ mTreeState = mFileTreeView .getSaveState ();
84+ } else {
85+ LOG .error ("Unable to save tree state. TreeView is null." );
86+ mTreeState = null ;
87+ }
88+ }
89+
90+ private void tryRestoreState () {
91+ tryRestoreState (mTreeState );
92+ }
93+
94+ private void tryRestoreState (String state ) {
95+ if (!TextUtils .isEmpty (state ) && mFileTreeView != null ) {
96+
97+ LOG .debug ("Restoring tree view state:" , "'" + state + "'" );
98+
99+ mFileTreeView .collapseAll ();
100+ final var openNodesArray = state .split (NODES_PATH_SEPARATOR );
101+ final var openNodes = new HashSet <>(Arrays .asList (openNodesArray ));
102+ restoreNodeState (mRoot , openNodes );
103+ } else {
104+ LOG .error (
105+ "Unable to restore tree state" ,
106+ "treeState=" + state ,
107+ "treeView=" + mFileTreeView );
108+ }
109+ }
110+
111+ private void restoreNodeState (@ NonNull TreeNode root , Set <String > openNodes ) {
112+ final var children = root .getChildren ();
113+ for (int i = 0 , childrenSize = children .size (); i < childrenSize ; i ++) {
114+ final var node = children .get (i );
115+ if (openNodes .contains (node .getPath ())) {
116+ listNode (
117+ node ,
118+ () -> {
119+ updateChevron (node );
120+ expandNode (node );
121+ restoreNodeState (node , openNodes );
122+ });
123+ }
124+ }
125+ }
126+
74127 @ Override
75128 public void onAttach (@ NonNull Context context ) {
76129 super .onAttach (context );
@@ -89,6 +142,10 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
89142 super .onViewCreated (view , savedInstanceState );
90143 mProject = requireArguments ().getParcelable ("project" );
91144
145+ if (savedInstanceState != null && savedInstanceState .containsKey (KEY_STORED_TREE_STATE )) {
146+ mTreeState = savedInstanceState .getString (KEY_STORED_TREE_STATE , null );
147+ }
148+
92149 listProjectFiles ();
93150 }
94151
@@ -121,41 +178,48 @@ public void onClick(TreeNode node, Object p2) {
121178 expandNode (node );
122179 } else {
123180 setLoading (node );
124- listNode (node );
181+ listNode (
182+ node ,
183+ () -> {
184+ updateChevron (node );
185+ expandNode (node );
186+ });
125187 }
126188 }
127189 }
128190
129- private void listNode (@ NonNull TreeNode node ) {
191+ @ Override
192+ public void onSaveInstanceState (@ NonNull Bundle outState ) {
193+ super .onSaveInstanceState (outState );
194+ saveTreeState ();
195+ outState .putString (KEY_STORED_TREE_STATE , mTreeState );
196+ }
197+
198+ private void listNode (@ NonNull TreeNode node , Runnable whenDone ) {
199+
200+ if (whenDone == null ) {
201+ whenDone = () -> {};
202+ }
203+
130204 node .getChildren ().clear ();
131205 node .setExpanded (false );
132- new TaskExecutor ()
133- .executeAsync (
134- () -> {
135- getNodeFromFiles (
136- node .getValue ()
137- .listFiles (
138- /* new FileTreeCallable.HiddenFilesFilter() */ ),
139- node );
140- TreeNode temp = node ;
141- while (temp .size () == 1 ) {
142- temp = temp .childAt (0 );
143- if (!temp .getValue ().isDirectory ()) {
144- break ;
145- }
146- getNodeFromFiles (
147- temp .getValue ()
148- .listFiles (
149- /* new FileTreeCallable.HiddenFilesFilter() */ ),
150- temp );
151- temp .setExpanded (true );
152- }
153- return null ;
154- },
155- __ -> {
156- updateChevron (node );
157- expandNode (node );
158- });
206+
207+ final var finalWhenDone = whenDone ;
208+ TaskExecutor .execAsync (
209+ () -> {
210+ getNodeFromFiles (node .getValue ().listFiles (), node );
211+ TreeNode temp = node ;
212+ while (temp .size () == 1 ) {
213+ temp = temp .childAt (0 );
214+ if (!temp .getValue ().isDirectory ()) {
215+ break ;
216+ }
217+ getNodeFromFiles (temp .getValue ().listFiles (), temp );
218+ temp .setExpanded (true );
219+ }
220+ return null ;
221+ },
222+ __ -> finalWhenDone .run ());
159223 }
160224
161225 private void getNodeFromFiles (File [] files , TreeNode parent ) {
@@ -246,7 +310,11 @@ public void listProjectFiles() {
246310 tree .setDefaultNodeClickListener (FileTreeFragment .this );
247311 tree .setDefaultNodeLongClickListener (FileTreeFragment .this );
248312 getScrollView ().removeAllViews ();
249- getScrollView ().addView (tree .getView ());
313+
314+ final var view = tree .getView ();
315+ getScrollView ().addView (view );
316+
317+ view .post (this ::tryRestoreState );
250318 }
251319 });
252320 }
0 commit comments