This is required for a future two-fragment tablet layout, and simplifies the code a bit since the profile detail (view/edit) will be implemented as fragments anyway. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>master
@@ -12,7 +12,7 @@ | |||
android:roundIcon="@mipmap/ic_launcher_round" | |||
android:supportsRtl="true" | |||
android:theme="@android:style/Theme.Material.Light.DarkActionBar"> | |||
<activity android:name=".ProfileListActivity"> | |||
<activity android:name=".ProfileActivity"> | |||
<intent-filter> | |||
<action android:name="android.intent.action.MAIN" /> | |||
@@ -5,49 +5,45 @@ import android.content.ComponentName; | |||
import android.content.Context; | |||
import android.content.Intent; | |||
import android.content.ServiceConnection; | |||
import android.databinding.DataBindingUtil; | |||
import android.os.Bundle; | |||
import android.os.IBinder; | |||
import android.view.Menu; | |||
import android.view.MenuItem; | |||
import android.view.View; | |||
import android.widget.AdapterView; | |||
import android.widget.ListView; | |||
import com.wireguard.android.databinding.ProfileListActivityBinding; | |||
import com.wireguard.config.Profile; | |||
public class ProfileListActivity extends Activity { | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* Activity that allows creating/viewing/editing/deleting WireGuard profiles. | |||
*/ | |||
public class ProfileActivity extends Activity { | |||
private final ServiceConnection connection = new ProfileServiceConnection(); | |||
private ProfileListActivityBinding binding; | |||
private final List<ServiceConnectionListener> listeners = new ArrayList<>(); | |||
private ProfileServiceInterface service; | |||
public void addServiceConnectionListener(ServiceConnectionListener listener) { | |||
listeners.add(listener); | |||
} | |||
public ProfileServiceInterface getService() { | |||
return service; | |||
} | |||
@Override | |||
protected void onCreate(Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
binding = DataBindingUtil.setContentView(this, R.layout.profile_list_activity); | |||
setContentView(R.layout.profile_activity); | |||
// Ensure the long-running service is started. This only needs to happen once. | |||
Intent intent = new Intent(this, ProfileService.class); | |||
startService(intent); | |||
ListView listView = findViewById(R.id.profile_list); | |||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { | |||
@Override | |||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { | |||
Profile profile = (Profile) parent.getItemAtPosition(position); | |||
if (profile == null || service == null) | |||
return; | |||
if (profile.getIsConnected()) | |||
service.disconnectProfile(profile); | |||
else | |||
service.connectProfile(profile); | |||
} | |||
}); | |||
} | |||
@Override | |||
public boolean onCreateOptionsMenu(Menu menu) { | |||
getMenuInflater().inflate(R.menu.profile_list, menu); | |||
getMenuInflater().inflate(R.menu.main, menu); | |||
return true; | |||
} | |||
@@ -55,6 +51,10 @@ public class ProfileListActivity extends Activity { | |||
} | |||
public void onProfileSelected(Profile profile) { | |||
} | |||
@Override | |||
public void onStart() { | |||
super.onStart(); | |||
@@ -67,20 +67,29 @@ public class ProfileListActivity extends Activity { | |||
super.onStop(); | |||
if (service != null) { | |||
unbindService(connection); | |||
for (ServiceConnectionListener listener : listeners) | |||
listener.onServiceDisconnected(); | |||
service = null; | |||
} | |||
} | |||
public void removeServiceConnectionListener(ServiceConnectionListener listener) { | |||
listeners.remove(listener); | |||
} | |||
private class ProfileServiceConnection implements ServiceConnection { | |||
@Override | |||
public void onServiceConnected(ComponentName component, IBinder binder) { | |||
service = (ProfileServiceInterface) binder; | |||
binding.setProfiles(service.getProfiles()); | |||
for (ServiceConnectionListener listener : listeners) | |||
listener.onServiceConnected(service); | |||
} | |||
@Override | |||
public void onServiceDisconnected(ComponentName component) { | |||
// This function is only called when the service crashes or goes away unexpectedly. | |||
for (ServiceConnectionListener listener : listeners) | |||
listener.onServiceDisconnected(); | |||
service = null; | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
package com.wireguard.android; | |||
import android.app.Fragment; | |||
import android.content.Context; | |||
import android.os.Bundle; | |||
import android.view.LayoutInflater; | |||
import android.view.View; | |||
import android.view.ViewGroup; | |||
import android.widget.AdapterView; | |||
import android.widget.ListView; | |||
import com.wireguard.android.databinding.ProfileListFragmentBinding; | |||
import com.wireguard.config.Profile; | |||
/** | |||
* Fragment containing the list of available WireGuard profiles. Must be part of a ProfileActivity. | |||
*/ | |||
public class ProfileListFragment extends Fragment implements ServiceConnectionListener { | |||
private ProfileActivity activity; | |||
private ProfileListFragmentBinding binding; | |||
private ProfileServiceInterface service; | |||
@Override | |||
public void onAttach(Context context) { | |||
super.onAttach(context); | |||
activity = (ProfileActivity) context; | |||
activity.addServiceConnectionListener(this); | |||
service = activity.getService(); | |||
} | |||
@Override | |||
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { | |||
binding = ProfileListFragmentBinding.inflate(inflater, parent, false); | |||
final ListView listView = (ListView) binding.getRoot(); | |||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { | |||
@Override | |||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { | |||
final Profile profile = (Profile) parent.getItemAtPosition(position); | |||
((ProfileActivity) getActivity()).onProfileSelected(profile); | |||
} | |||
}); | |||
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { | |||
@Override | |||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, | |||
long id) { | |||
final Profile profile = (Profile) parent.getItemAtPosition(position); | |||
if (profile == null || service == null) | |||
return false; | |||
if (profile.getIsConnected()) | |||
service.disconnectProfile(profile); | |||
else | |||
service.connectProfile(profile); | |||
return true; | |||
} | |||
}); | |||
return binding.getRoot(); | |||
} | |||
@Override | |||
public void onDetach() { | |||
super.onDetach(); | |||
activity.removeServiceConnectionListener(this); | |||
} | |||
@Override | |||
public void onServiceConnected(ProfileServiceInterface service) { | |||
this.service = service; | |||
binding.setProfiles(service.getProfiles()); | |||
} | |||
@Override | |||
public void onServiceDisconnected() { | |||
service = null; | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
package com.wireguard.android; | |||
/** | |||
* Interface for fragments that need notification about connection changes to the ProfileService. | |||
*/ | |||
interface ServiceConnectionListener { | |||
void onServiceConnected(ProfileServiceInterface service); | |||
void onServiceDisconnected(); | |||
} |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<fragment xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:id="@+id/profile_list_fragment" | |||
android:name="com.wireguard.android.ProfileListFragment" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" /> |