]> git.ipfire.org Git - thirdparty/strongswan.git/blob
e2d949a68c8bde4ee023b76d39eac8f2ce7be583
[thirdparty/strongswan.git] /
1 /*
2 * Copyright (C) 2017 Tobias Brunner
3 *
4 * Copyright (C) secunet Security Networks AG
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 package org.strongswan.android.ui;
18
19 import android.Manifest;
20 import android.content.Context;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.PackageManager;
23 import android.os.Bundle;
24 import android.text.TextUtils;
25 import android.util.Pair;
26 import android.view.Menu;
27 import android.view.MenuInflater;
28 import android.view.MenuItem;
29 import android.view.View;
30 import android.widget.Filter;
31 import android.widget.ListView;
32
33 import org.strongswan.android.R;
34 import org.strongswan.android.data.VpnProfileDataSource;
35 import org.strongswan.android.ui.adapter.SelectedApplicationEntry;
36 import org.strongswan.android.ui.adapter.SelectedApplicationsAdapter;
37
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.SortedSet;
42 import java.util.TreeSet;
43
44 import androidx.annotation.NonNull;
45 import androidx.annotation.Nullable;
46 import androidx.appcompat.widget.SearchView;
47 import androidx.fragment.app.ListFragment;
48 import androidx.loader.app.LoaderManager;
49 import androidx.loader.content.AsyncTaskLoader;
50 import androidx.loader.content.Loader;
51
52 public class SelectedApplicationsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Pair<List<SelectedApplicationEntry>, List<String>>>, SearchView.OnQueryTextListener
53 {
54 private SelectedApplicationsAdapter mAdapter;
55 private SortedSet<String> mSelection;
56
57 @Override
58 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
59 {
60 super.onViewCreated(view, savedInstanceState);
61 setHasOptionsMenu(true);
62
63 getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
64
65 mAdapter = new SelectedApplicationsAdapter(getActivity());
66 setListAdapter(mAdapter);
67 setListShown(false);
68
69 ArrayList<String> selection;
70 if (savedInstanceState == null)
71 {
72 selection = getActivity().getIntent().getStringArrayListExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
73 }
74 else
75 {
76 selection = savedInstanceState.getStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
77 }
78 mSelection = new TreeSet<>(selection);
79
80 LoaderManager.getInstance(this).initLoader(0, null, this);
81 }
82
83 @Override
84 public void onSaveInstanceState(Bundle outState)
85 {
86 super.onSaveInstanceState(outState);
87 outState.putStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelection));
88 }
89
90 /**
91 * Returns the package names of all selected apps
92 */
93 public ArrayList<String> getSelectedApplications()
94 {
95 return new ArrayList<>(mSelection);
96 }
97
98 /**
99 * Track check state as ListView is unable to do that when using filters
100 */
101 @Override
102 public void onListItemClick(ListView l, View v, int position, long id)
103 {
104 super.onListItemClick(l, v, position, id);
105 SelectedApplicationEntry item = (SelectedApplicationEntry)getListView().getItemAtPosition(position);
106 item.setSelected(!item.isSelected());
107 if (item.isSelected())
108 {
109 mSelection.add(item.getInfo().packageName);
110 }
111 else
112 {
113 mSelection.remove(item.getInfo().packageName);
114 }
115 }
116
117 /**
118 * Manually set the check state as ListView is unable to track that when using filters
119 */
120 private void setCheckState()
121 {
122 for (int i = 0; i < getListView().getCount(); i++)
123 {
124 SelectedApplicationEntry item = mAdapter.getItem(i);
125 getListView().setItemChecked(i, item.isSelected());
126 }
127 }
128
129 @Override
130 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
131 {
132 MenuItem item = menu.add(R.string.search);
133 item.setIcon(android.R.drawable.ic_menu_search);
134 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
135
136 SearchView sv = new SearchView(getActivity());
137 sv.setOnQueryTextListener(this);
138 item.setActionView(sv);
139
140 super.onCreateOptionsMenu(menu, inflater);
141 }
142
143 @Override
144 public Loader<Pair<List<SelectedApplicationEntry>, List<String>>> onCreateLoader(int id, Bundle args)
145 {
146 return new InstalledPackagesLoader(getActivity(), mSelection);
147 }
148
149 @Override
150 public void onLoadFinished(Loader<Pair<List<SelectedApplicationEntry>, List<String>>> loader, Pair<List<SelectedApplicationEntry>, List<String>> data)
151 {
152 mAdapter.setData(data.first);
153 mSelection.removeAll(data.second);
154 setCheckState();
155
156 if (isResumed())
157 {
158 setListShown(true);
159 }
160 else
161 {
162 setListShownNoAnimation(true);
163 }
164 }
165
166 @Override
167 public void onLoaderReset(Loader<Pair<List<SelectedApplicationEntry>, List<String>>> loader)
168 {
169 mAdapter.setData(null);
170 }
171
172 @Override
173 public boolean onQueryTextSubmit(String query)
174 {
175 return true;
176 }
177
178 @Override
179 public boolean onQueryTextChange(String newText)
180 {
181 String search = TextUtils.isEmpty(newText) ? null : newText;
182 mAdapter.getFilter().filter(search, new Filter.FilterListener()
183 {
184 @Override
185 public void onFilterComplete(int count)
186 {
187 setCheckState();
188 }
189 });
190 return true;
191 }
192
193 public static class InstalledPackagesLoader extends AsyncTaskLoader<Pair<List<SelectedApplicationEntry>, List<String>>>
194 {
195 private final PackageManager mPackageManager;
196 private final SortedSet<String> mSelection;
197 private Pair<List<SelectedApplicationEntry>, List<String>> mData;
198
199 public InstalledPackagesLoader(Context context, SortedSet<String> selection)
200 {
201 super(context);
202 mPackageManager = context.getPackageManager();
203 mSelection = selection;
204 }
205
206 @Override
207 public Pair<List<SelectedApplicationEntry>, List<String>> loadInBackground()
208 {
209 List<SelectedApplicationEntry> apps = new ArrayList<>();
210 SortedSet<String> seen = new TreeSet<>();
211 for (ApplicationInfo info : mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA))
212 {
213 /* skip apps that can't access the network anyway */
214 if (mPackageManager.checkPermission(Manifest.permission.INTERNET, info.packageName) == PackageManager.PERMISSION_GRANTED)
215 {
216 SelectedApplicationEntry entry = new SelectedApplicationEntry(mPackageManager, info);
217 entry.setSelected(mSelection.contains(info.packageName));
218 apps.add(entry);
219 seen.add(info.packageName);
220 }
221 }
222 Collections.sort(apps);
223 /* check for selected packages that don't exist anymore */
224 List<String> missing = new ArrayList<>();
225 for (String pkg : mSelection)
226 {
227 if (!seen.contains(pkg))
228 {
229 missing.add(pkg);
230 }
231 }
232 return new Pair<>(apps, missing);
233 }
234
235 @Override
236 protected void onStartLoading()
237 {
238 if (mData != null)
239 { /* if we have data ready, deliver it directly */
240 deliverResult(mData);
241 }
242 if (takeContentChanged() || mData == null)
243 {
244 forceLoad();
245 }
246 }
247
248 @Override
249 public void deliverResult(Pair<List<SelectedApplicationEntry>, List<String>> data)
250 {
251 if (isReset())
252 {
253 return;
254 }
255 mData = data;
256 if (isStarted())
257 { /* if it is started we deliver the data directly,
258 * otherwise this is handled in onStartLoading */
259 super.deliverResult(data);
260 }
261 }
262
263 @Override
264 protected void onReset()
265 {
266 mData = null;
267 super.onReset();
268 }
269 }
270 }