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