349 lines
10 KiB
Java
349 lines
10 KiB
Java
/*
|
|
* Copyright (C) 2012-2019 Tobias Brunner
|
|
* Copyright (C) 2012 Giuliano Grassi
|
|
* Copyright (C) 2012 Ralf Sager
|
|
* HSR Hochschule fuer Technik Rapperswil
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
package org.strongswan.android.ui;
|
|
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.res.TypedArray;
|
|
import android.os.Bundle;
|
|
import android.util.AttributeSet;
|
|
import android.view.ActionMode;
|
|
import android.view.LayoutInflater;
|
|
import android.view.Menu;
|
|
import android.view.MenuInflater;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.AbsListView.MultiChoiceModeListener;
|
|
import android.widget.AdapterView;
|
|
import android.widget.AdapterView.OnItemClickListener;
|
|
import android.widget.ListView;
|
|
import android.widget.Toast;
|
|
|
|
import org.strongswan.android.R;
|
|
import org.strongswan.android.data.VpnProfile;
|
|
import org.strongswan.android.data.VpnProfileDataSource;
|
|
import org.strongswan.android.ui.adapter.VpnProfileAdapter;
|
|
import org.strongswan.android.utils.Constants;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
|
|
import androidx.fragment.app.Fragment;
|
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
|
|
public class VpnProfileListFragment extends Fragment
|
|
{
|
|
private static final String SELECTED_KEY = "SELECTED";
|
|
|
|
private List<VpnProfile> mVpnProfiles;
|
|
private VpnProfileDataSource mDataSource;
|
|
private VpnProfileAdapter mListAdapter;
|
|
private ListView mListView;
|
|
private OnVpnProfileSelectedListener mListener;
|
|
private HashSet<Integer> mSelected;
|
|
private boolean mReadOnly;
|
|
|
|
private BroadcastReceiver mProfilesChanged = new BroadcastReceiver()
|
|
{
|
|
@Override
|
|
public void onReceive(Context context, Intent intent)
|
|
{
|
|
long id, ids[];
|
|
if ((id = intent.getLongExtra(Constants.VPN_PROFILES_SINGLE, 0)) > 0)
|
|
{
|
|
VpnProfile profile = mDataSource.getVpnProfile(id);
|
|
if (profile != null)
|
|
{ /* in case this was an edit, we remove it first */
|
|
mVpnProfiles.remove(profile);
|
|
mVpnProfiles.add(profile);
|
|
mListAdapter.notifyDataSetChanged();
|
|
}
|
|
}
|
|
else if ((ids = intent.getLongArrayExtra(Constants.VPN_PROFILES_MULTIPLE)) != null)
|
|
{
|
|
for (long i : ids)
|
|
{
|
|
Iterator<VpnProfile> profiles = mVpnProfiles.iterator();
|
|
while (profiles.hasNext())
|
|
{
|
|
VpnProfile profile = profiles.next();
|
|
if (profile.getId() == i)
|
|
{
|
|
profiles.remove();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mListAdapter.notifyDataSetChanged();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The activity containing this fragment should implement this interface
|
|
*/
|
|
public interface OnVpnProfileSelectedListener {
|
|
void onVpnProfileSelected(VpnProfile profile);
|
|
}
|
|
|
|
@Override
|
|
public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState)
|
|
{
|
|
super.onInflate(context, attrs, savedInstanceState);
|
|
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Fragment);
|
|
mReadOnly = a.getBoolean(R.styleable.Fragment_read_only, false);
|
|
a.recycle();
|
|
}
|
|
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
Bundle savedInstanceState)
|
|
{
|
|
View view = inflater.inflate(R.layout.profile_list_fragment, null);
|
|
|
|
mListView = view.findViewById(R.id.profile_list);
|
|
mListView.setAdapter(mListAdapter);
|
|
mListView.setEmptyView(view.findViewById(R.id.profile_list_empty));
|
|
mListView.setOnItemClickListener(mVpnProfileClicked);
|
|
|
|
if (!mReadOnly)
|
|
{
|
|
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
|
mListView.setMultiChoiceModeListener(mVpnProfileSelected);
|
|
}
|
|
return view;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState)
|
|
{
|
|
super.onCreate(savedInstanceState);
|
|
|
|
Bundle args = getArguments();
|
|
if (args != null)
|
|
{
|
|
mReadOnly = args.getBoolean("read_only", mReadOnly);
|
|
}
|
|
|
|
if (!mReadOnly)
|
|
{
|
|
setHasOptionsMenu(true);
|
|
|
|
ArrayList<Integer> selected = null;
|
|
if (savedInstanceState != null)
|
|
{
|
|
selected = savedInstanceState.getIntegerArrayList(SELECTED_KEY);
|
|
}
|
|
mSelected = selected != null ? new HashSet<>(selected) : new HashSet<>();
|
|
}
|
|
|
|
mDataSource = new VpnProfileDataSource(this.getActivity());
|
|
mDataSource.open();
|
|
|
|
/* cached list of profiles used as backend for the ListView */
|
|
mVpnProfiles = mDataSource.getAllVpnProfiles();
|
|
|
|
mListAdapter = new VpnProfileAdapter(getActivity(), R.layout.profile_list_item, mVpnProfiles);
|
|
|
|
IntentFilter profileChangesFilter = new IntentFilter(Constants.VPN_PROFILES_CHANGED);
|
|
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mProfilesChanged, profileChangesFilter);
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(Bundle outState)
|
|
{
|
|
super.onSaveInstanceState(outState);
|
|
outState.putIntegerArrayList(SELECTED_KEY, new ArrayList<>(mSelected));
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy()
|
|
{
|
|
super.onDestroy();
|
|
mDataSource.close();
|
|
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mProfilesChanged);
|
|
}
|
|
|
|
@Override
|
|
public void onAttach(Context context)
|
|
{
|
|
super.onAttach(context);
|
|
|
|
if (context instanceof OnVpnProfileSelectedListener)
|
|
{
|
|
mListener = (OnVpnProfileSelectedListener)context;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
|
|
{
|
|
inflater.inflate(R.menu.profile_list, menu);
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item)
|
|
{
|
|
switch (item.getItemId())
|
|
{
|
|
case R.id.add_profile:
|
|
Intent connectionIntent = new Intent(getActivity(),
|
|
VpnProfileDetailActivity.class);
|
|
startActivity(connectionIntent);
|
|
return true;
|
|
default:
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
}
|
|
|
|
private final OnItemClickListener mVpnProfileClicked = new OnItemClickListener() {
|
|
@Override
|
|
public void onItemClick(AdapterView<?> a, View v, int position, long id)
|
|
{
|
|
if (mListener != null)
|
|
{
|
|
mListener.onVpnProfileSelected((VpnProfile)a.getItemAtPosition(position));
|
|
}
|
|
}
|
|
};
|
|
|
|
private final MultiChoiceModeListener mVpnProfileSelected = new MultiChoiceModeListener() {
|
|
private MenuItem mEditProfile;
|
|
private MenuItem mCopyProfile;
|
|
|
|
@Override
|
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
|
|
{
|
|
mEditProfile.setEnabled(mSelected.size() == 1);
|
|
mCopyProfile.setEnabled(mEditProfile.isEnabled());
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onDestroyActionMode(ActionMode mode)
|
|
{
|
|
mSelected.clear();
|
|
}
|
|
|
|
@Override
|
|
public boolean onCreateActionMode(ActionMode mode, Menu menu)
|
|
{
|
|
MenuInflater inflater = mode.getMenuInflater();
|
|
inflater.inflate(R.menu.profile_list_context, menu);
|
|
mEditProfile = menu.findItem(R.id.edit_profile);
|
|
mCopyProfile = menu.findItem(R.id.copy_profile);
|
|
mode.setTitle(R.string.select_profiles);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item)
|
|
{
|
|
switch (item.getItemId())
|
|
{
|
|
case R.id.edit_profile:
|
|
{
|
|
int position = mSelected.iterator().next();
|
|
VpnProfile profile = (VpnProfile)mListView.getItemAtPosition(position);
|
|
Intent connectionIntent = new Intent(getActivity(), VpnProfileDetailActivity.class);
|
|
connectionIntent.putExtra(VpnProfileDataSource.KEY_ID, profile.getId());
|
|
startActivity(connectionIntent);
|
|
break;
|
|
}
|
|
case R.id.copy_profile:
|
|
{
|
|
int position = mSelected.iterator().next();
|
|
VpnProfile profile = (VpnProfile)mListView.getItemAtPosition(position);
|
|
profile = profile.clone();
|
|
profile.setUUID(UUID.randomUUID());
|
|
profile.setName(String.format(getString(R.string.copied_name), profile.getName()));
|
|
mDataSource.insertProfile(profile);
|
|
|
|
Intent intent = new Intent(Constants.VPN_PROFILES_CHANGED);
|
|
intent.putExtra(Constants.VPN_PROFILES_SINGLE, profile.getId());
|
|
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
|
|
|
|
Intent connectionIntent = new Intent(getActivity(), VpnProfileDetailActivity.class);
|
|
connectionIntent.putExtra(VpnProfileDataSource.KEY_ID, profile.getId());
|
|
startActivity(connectionIntent);
|
|
break;
|
|
}
|
|
case R.id.delete_profile:
|
|
{
|
|
ArrayList<VpnProfile> profiles = new ArrayList<>();
|
|
for (int position : mSelected)
|
|
{
|
|
profiles.add((VpnProfile)mListView.getItemAtPosition(position));
|
|
}
|
|
long ids[] = new long[profiles.size()];
|
|
for (int i = 0; i < profiles.size(); i++)
|
|
{
|
|
VpnProfile profile = profiles.get(i);
|
|
ids[i] = profile.getId();
|
|
mDataSource.deleteVpnProfile(profile);
|
|
}
|
|
Intent intent = new Intent(Constants.VPN_PROFILES_CHANGED);
|
|
intent.putExtra(Constants.VPN_PROFILES_MULTIPLE, ids);
|
|
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
|
|
Toast.makeText(VpnProfileListFragment.this.getActivity(),
|
|
R.string.profiles_deleted, Toast.LENGTH_SHORT).show();
|
|
break;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
mode.finish();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onItemCheckedStateChanged(ActionMode mode, int position,
|
|
long id, boolean checked)
|
|
{
|
|
if (checked)
|
|
{
|
|
mSelected.add(position);
|
|
}
|
|
else
|
|
{
|
|
mSelected.remove(position);
|
|
}
|
|
final int checkedCount = mSelected.size();
|
|
switch (checkedCount)
|
|
{
|
|
case 0:
|
|
mode.setSubtitle(R.string.no_profile_selected);
|
|
break;
|
|
case 1:
|
|
mode.setSubtitle(R.string.one_profile_selected);
|
|
break;
|
|
default:
|
|
mode.setSubtitle(String.format(getString(R.string.x_profiles_selected), checkedCount));
|
|
break;
|
|
}
|
|
mode.invalidate();
|
|
}
|
|
};
|
|
}
|