2 * Copyright (C) 2017 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
16 package org.strongswan.android.ui;
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;
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;
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;
49 public class SelectedApplicationsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Pair<List<SelectedApplicationEntry>, List<String>>>, SearchView.OnQueryTextListener
51 private SelectedApplicationsAdapter mAdapter;
52 private SortedSet<String> mSelection;
55 public void onActivityCreated(@Nullable Bundle savedInstanceState)
57 super.onActivityCreated(savedInstanceState);
58 setHasOptionsMenu(true);
60 getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
62 mAdapter = new SelectedApplicationsAdapter(getActivity());
63 setListAdapter(mAdapter);
66 ArrayList<String> selection;
67 if (savedInstanceState == null)
69 selection = getActivity().getIntent().getStringArrayListExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
73 selection = savedInstanceState.getStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
75 mSelection = new TreeSet<>(selection);
77 getLoaderManager().initLoader(0, null, this);
81 public void onSaveInstanceState(Bundle outState)
83 super.onSaveInstanceState(outState);
84 outState.putStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelection));
88 * Returns the package names of all selected apps
90 public ArrayList<String> getSelectedApplications()
92 return new ArrayList<>(mSelection);
96 * Track check state as ListView is unable to do that when using filters
99 public void onListItemClick(ListView l, View v, int position, long id)
101 super.onListItemClick(l, v, position, id);
102 SelectedApplicationEntry item = (SelectedApplicationEntry)getListView().getItemAtPosition(position);
103 item.setSelected(!item.isSelected());
104 if (item.isSelected())
106 mSelection.add(item.getInfo().packageName);
110 mSelection.remove(item.getInfo().packageName);
115 * Manually set the check state as ListView is unable to track that when using filters
117 private void setCheckState()
119 for (int i = 0; i < getListView().getCount(); i++)
121 SelectedApplicationEntry item = mAdapter.getItem(i);
122 getListView().setItemChecked(i, item.isSelected());
127 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
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);
133 SearchView sv = new SearchView(getActivity());
134 sv.setOnQueryTextListener(this);
135 item.setActionView(sv);
137 super.onCreateOptionsMenu(menu, inflater);
141 public Loader<Pair<List<SelectedApplicationEntry>, List<String>>> onCreateLoader(int id, Bundle args)
143 return new InstalledPackagesLoader(getActivity(), mSelection);
147 public void onLoadFinished(Loader<Pair<List<SelectedApplicationEntry>, List<String>>> loader, Pair<List<SelectedApplicationEntry>, List<String>> data)
149 mAdapter.setData(data.first);
150 mSelection.removeAll(data.second);
159 setListShownNoAnimation(true);
164 public void onLoaderReset(Loader<Pair<List<SelectedApplicationEntry>, List<String>>> loader)
166 mAdapter.setData(null);
170 public boolean onQueryTextSubmit(String query)
176 public boolean onQueryTextChange(String newText)
178 String search = TextUtils.isEmpty(newText) ? null : newText;
179 mAdapter.getFilter().filter(search, new Filter.FilterListener()
182 public void onFilterComplete(int count)
190 public static class InstalledPackagesLoader extends AsyncTaskLoader<Pair<List<SelectedApplicationEntry>, List<String>>>
192 private final PackageManager mPackageManager;
193 private final SortedSet<String> mSelection;
194 private Pair<List<SelectedApplicationEntry>, List<String>> mData;
196 public InstalledPackagesLoader(Context context, SortedSet<String> selection)
199 mPackageManager = context.getPackageManager();
200 mSelection = selection;
204 public Pair<List<SelectedApplicationEntry>, List<String>> loadInBackground()
206 List<SelectedApplicationEntry> apps = new ArrayList<>();
207 SortedSet<String> seen = new TreeSet<>();
208 for (ApplicationInfo info : mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA))
210 /* skip apps that can't access the network anyway */
211 if (mPackageManager.checkPermission(Manifest.permission.INTERNET, info.packageName) == PackageManager.PERMISSION_GRANTED)
213 SelectedApplicationEntry entry = new SelectedApplicationEntry(mPackageManager, info);
214 entry.setSelected(mSelection.contains(info.packageName));
216 seen.add(info.packageName);
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)
224 if (!seen.contains(pkg))
229 return new Pair<>(apps, missing);
233 protected void onStartLoading()
236 { /* if we have data ready, deliver it directly */
237 deliverResult(mData);
239 if (takeContentChanged() || mData == null)
246 public void deliverResult(Pair<List<SelectedApplicationEntry>, List<String>> data)
254 { /* if it is started we deliver the data directly,
255 * otherwise this is handled in onStartLoading */
256 super.deliverResult(data);
261 protected void onReset()