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