diff options
Diffstat (limited to 'remoteExample')
20 files changed, 734 insertions, 0 deletions
diff --git a/remoteExample/build.gradle b/remoteExample/build.gradle new file mode 100644 index 00000000..59e37fbd --- /dev/null +++ b/remoteExample/build.gradle @@ -0,0 +1,32 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.9.+' + } +} + +apply plugin: 'android' + +android { + compileSdkVersion 19 + buildToolsVersion "19.0.3" + + defaultConfig { + minSdkVersion 15 + targetSdkVersion 19 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar', '*.aar']) +} diff --git a/remoteExample/src/main/AndroidManifest.xml b/remoteExample/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1d76db58 --- /dev/null +++ b/remoteExample/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="de.blinkt.openvpn.remote" > + + <permission + android:name="de.blinkt.openvpn.REMOTE_API" + android:description="@string/permission_description" + android:label="Control OpenVPN" + android:permissionGroup="android.permission-group.NETWORK" + android:protectionLevel="dangerous" /> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="de.blinkt.openvpn.REMOTE_API" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name="de.blinkt.openvpn.remote.MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/remoteExample/src/main/aidl/de/blinkt/openvpn/api/APIVpnProfile.aidl b/remoteExample/src/main/aidl/de/blinkt/openvpn/api/APIVpnProfile.aidl new file mode 100644 index 00000000..f6799659 --- /dev/null +++ b/remoteExample/src/main/aidl/de/blinkt/openvpn/api/APIVpnProfile.aidl @@ -0,0 +1,3 @@ +package de.blinkt.openvpn.api; + +parcelable APIVpnProfile; diff --git a/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl b/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl new file mode 100644 index 00000000..794e3aad --- /dev/null +++ b/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl @@ -0,0 +1,50 @@ +// IOpenVPNAPIService.aidl
+package de.blinkt.openvpn.api;
+
+import de.blinkt.openvpn.api.APIVpnProfile;
+import de.blinkt.openvpn.api.IOpenVPNStatusCallback;
+
+import android.content.Intent;
+import android.os.ParcelFileDescriptor;
+
+interface IOpenVPNAPIService {
+ List<APIVpnProfile> getProfiles();
+
+ void startProfile (String profileUUID);
+
+ /** Use a profile with all certificates etc. embedded */
+ boolean addVPNProfile (String name, String config);
+
+ /** start a profile using an config */
+ void startVPN (String inlineconfig);
+
+ /** This permission framework is used to avoid confused deputy style attack to the VPN
+ * calling this will give null if the app is allowed to use the external API and an Intent
+ * that can be launched to request permissions otherwise */
+ Intent prepare (String packagename);
+
+ /** Used to trigger to the Android VPN permission dialog (VPNService.prepare()) in advance,
+ * if this return null OpenVPN for ANdroid already has the permissions otherwise you can start the returned Intent
+ * to let OpenVPN for Android request the permission */
+ Intent prepareVPNService ();
+
+ /* Disconnect the VPN */
+ void disconnect();
+
+ /* Pause the VPN (same as using the pause feature in the notifcation bar) */
+ void pause();
+
+ /* Resume the VPN (same as using the pause feature in the notifcation bar) */
+ void resume();
+
+ /**
+ * Registers to receive OpenVPN Status Updates
+ */
+ void registerStatusCallback(IOpenVPNStatusCallback cb);
+
+ /**
+ * Remove a previously registered callback interface.
+ */
+ void unregisterStatusCallback(IOpenVPNStatusCallback cb);
+
+}
\ No newline at end of file diff --git a/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNStatusCallback.aidl b/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNStatusCallback.aidl new file mode 100644 index 00000000..1dfa1381 --- /dev/null +++ b/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNStatusCallback.aidl @@ -0,0 +1,13 @@ +package de.blinkt.openvpn.api;
+
+/**
+ * Example of a callback interface used by IRemoteService to send
+ * synchronous notifications back to its clients. Note that this is a
+ * one-way interface so the server does not block waiting for the client.
+ */
+oneway interface IOpenVPNStatusCallback {
+ /**
+ * Called when the service has a new status for you.
+ */
+ void newStatus(String uuid, String state, String message, String level);
+}
diff --git a/remoteExample/src/main/assets/hd.conf b/remoteExample/src/main/assets/hd.conf new file mode 100644 index 00000000..3dc917aa --- /dev/null +++ b/remoteExample/src/main/assets/hd.conf @@ -0,0 +1,61 @@ +# This config does not work, +# it only thought as demo to show starting a profile +client + +proto tcp +dev tun +topology subnet + +remote openvpn.blinkt.de 2000 +tls-remote openvpn.blinkt.de + +<auth-user-pass> +wronguser +wrongpassword +</auth-user-pass> + +comp-lzo +<ca> +-----BEGIN CERTIFICATE----- +MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290 +IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB +IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA +Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO +BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi +MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ +ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ +8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6 +zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y +fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7 +w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc +G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k +epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q +laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ +QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU +fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826 +YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w +ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY +gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe +MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0 +IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy +dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw +czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0 +dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl +aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC +AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg +b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB +ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc +nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg +18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c +gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl +Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY +sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T +SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF +CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum +GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk +zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW +omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD +-----END CERTIFICATE----- +</ca> +verb 2 diff --git a/remoteExample/src/main/ic_launcher2-web.png b/remoteExample/src/main/ic_launcher2-web.png Binary files differnew file mode 100644 index 00000000..5c63bc5e --- /dev/null +++ b/remoteExample/src/main/ic_launcher2-web.png diff --git a/remoteExample/src/main/java/de/blinkt/openvpn/api/APIVpnProfile.java b/remoteExample/src/main/java/de/blinkt/openvpn/api/APIVpnProfile.java new file mode 100644 index 00000000..f5591764 --- /dev/null +++ b/remoteExample/src/main/java/de/blinkt/openvpn/api/APIVpnProfile.java @@ -0,0 +1,51 @@ +package de.blinkt.openvpn.api;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class APIVpnProfile implements Parcelable {
+
+ public final String mUUID;
+ public final String mName;
+ public final boolean mUserEditable;
+
+ public APIVpnProfile(Parcel in) {
+ mUUID = in.readString();
+ mName = in.readString();
+ mUserEditable = in.readInt() != 0;
+ }
+
+ public APIVpnProfile(String uuidString, String name, boolean userEditable) {
+ mUUID=uuidString;
+ mName = name;
+ mUserEditable=userEditable;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mUUID);
+ dest.writeString(mName);
+ if(mUserEditable)
+ dest.writeInt(0);
+ else
+ dest.writeInt(1);
+ }
+
+ public static final Parcelable.Creator<APIVpnProfile> CREATOR
+ = new Parcelable.Creator<APIVpnProfile>() {
+ public APIVpnProfile createFromParcel(Parcel in) {
+ return new APIVpnProfile(in);
+ }
+
+ public APIVpnProfile[] newArray(int size) {
+ return new APIVpnProfile[size];
+ }
+ };
+
+
+}
diff --git a/remoteExample/src/main/java/de/blinkt/openvpn/remote/MainActivity.java b/remoteExample/src/main/java/de/blinkt/openvpn/remote/MainActivity.java new file mode 100644 index 00000000..900383a2 --- /dev/null +++ b/remoteExample/src/main/java/de/blinkt/openvpn/remote/MainActivity.java @@ -0,0 +1,51 @@ +package de.blinkt.openvpn.remote; + +import android.app.Activity; +import android.app.ActionBar; +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.os.Build; + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.container, new MainFragment()) + .commit(); + } + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + if (id == R.id.action_settings) { + return true; + } + return super.onOptionsItemSelected(item); + } + + + +} diff --git a/remoteExample/src/main/java/de/blinkt/openvpn/remote/MainFragment.java b/remoteExample/src/main/java/de/blinkt/openvpn/remote/MainFragment.java new file mode 100644 index 00000000..2ea37861 --- /dev/null +++ b/remoteExample/src/main/java/de/blinkt/openvpn/remote/MainFragment.java @@ -0,0 +1,321 @@ +package de.blinkt.openvpn.remote; + +import android.app.Activity; +import android.app.Fragment; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.List; + +import de.blinkt.openvpn.api.APIVpnProfile; +import de.blinkt.openvpn.api.IOpenVPNAPIService; +import de.blinkt.openvpn.api.IOpenVPNStatusCallback; + +public class MainFragment extends Fragment implements View.OnClickListener, Handler.Callback { + + private TextView mHelloWorld; + private Button mStartVpn; + private TextView mMyIp; + private TextView mStatus; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_main, container, false); + v.findViewById(R.id.disconnect).setOnClickListener(this); + v.findViewById(R.id.getMyIP).setOnClickListener(this); + v.findViewById(R.id.startembedded).setOnClickListener(this); + mHelloWorld = (TextView) v.findViewById(R.id.helloworld); + mStartVpn = (Button) v.findViewById(R.id.startVPN); + mStatus = (TextView) v.findViewById(R.id.status); + mMyIp = (TextView) v.findViewById(R.id.MyIpText); + + + return v; + + } + + private static final int MSG_UPDATE_STATE = 0; + private static final int MSG_UPDATE_MYIP = 1; + private static final int START_PROFILE_EMBEDDED = 2; + private static final int START_PROFILE_BYUUID = 3; + private static final int ICS_OPENVPN_PERMISSION = 7; + + protected IOpenVPNAPIService mService=null; + private Handler mHandler; + + + + + private void startEmbeddedProfile() + { + try { + InputStream conf = getActivity().getAssets().open("test.conf"); + InputStreamReader isr = new InputStreamReader(conf); + BufferedReader br = new BufferedReader(isr); + String config=""; + String line; + while(true) { + line = br.readLine(); + if(line == null) + break; + config += line + "\n"; + } + br.readLine(); + + // mService.addVPNProfile("test", config); + mService.startVPN(config); + } catch (IOException e) { + e.printStackTrace(); + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void onStart() { + super.onStart(); + mHandler = new Handler(this); + bindService(); + } + + + private IOpenVPNStatusCallback mCallback = new IOpenVPNStatusCallback.Stub() { + /** + * This is called by the remote service regularly to tell us about + * new values. Note that IPC calls are dispatched through a thread + * pool running in each process, so the code executing here will + * NOT be running in our main thread like most other things -- so, + * to update the UI, we need to use a Handler to hop over there. + */ + + @Override + public void newStatus(String uuid, String state, String message, String level) + throws RemoteException { + Message msg = Message.obtain(mHandler, MSG_UPDATE_STATE, state + "|" + message); + msg.sendToTarget(); + + } + + }; + + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, + IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. We are communicating with our + // service through an IDL interface, so get a client-side + // representation of that from the raw service object. + + mService = IOpenVPNAPIService.Stub.asInterface(service); + + try { + // Request permission to use the API + Intent i = mService.prepare(getActivity().getPackageName()); + if (i!=null) { + startActivityForResult(i, ICS_OPENVPN_PERMISSION); + } else { + onActivityResult(ICS_OPENVPN_PERMISSION, Activity.RESULT_OK,null); + } + + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mService = null; + + } + }; + private String mStartUUID=null; + + private void bindService() { + getActivity().bindService(new Intent(IOpenVPNAPIService.class.getName()), + mConnection, Context.BIND_AUTO_CREATE); + } + + protected void listVPNs() { + + try { + List<APIVpnProfile> list = mService.getProfiles(); + String all="List:"; + for(APIVpnProfile vp:list) { + all = all + vp.mName + ":" + vp.mUUID + "\n"; + } + + if(list.size()> 0) { + Button b= mStartVpn; + b.setOnClickListener(this); + b.setVisibility(View.VISIBLE); + b.setText(list.get(0).mName); + mStartUUID = list.get(0).mUUID; + } + + + + mHelloWorld.setText(all); + + } catch (RemoteException e) { + // TODO Auto-generated catch block + mHelloWorld.setText(e.getMessage()); + } + } + + private void unbindService() { + getActivity().unbindService(mConnection); + } + + @Override + public void onStop() { + super.onStop(); + unbindService(); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.startVPN: + try { + prepareStartProfile(START_PROFILE_BYUUID); + } catch (RemoteException e) { + e.printStackTrace(); + } + break; + case R.id.disconnect: + try { + mService.disconnect(); + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + case R.id.getMyIP: + + // Socket handling is not allowed on main thread + new Thread() { + + @Override + public void run() { + try { + String myip = getMyOwnIP(); + Message msg = Message.obtain(mHandler,MSG_UPDATE_MYIP,myip); + msg.sendToTarget(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + }.start(); + + break; + case R.id.startembedded: + try { + prepareStartProfile(START_PROFILE_EMBEDDED); + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + default: + break; + } + + } + + private void prepareStartProfile(int requestCode) throws RemoteException { + Intent requestpermission = mService.prepareVPNService(); + if(requestpermission == null) { + onActivityResult(requestCode, Activity.RESULT_OK, null); + } else { + // Have to call an external Activity since services cannot used onActivityResult + startActivityForResult(requestpermission, requestCode); + } + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + if(requestCode==START_PROFILE_EMBEDDED) + startEmbeddedProfile(); + if(requestCode==START_PROFILE_BYUUID) + try { + mService.startProfile(mStartUUID); + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (requestCode == ICS_OPENVPN_PERMISSION) { + listVPNs(); + try { + mService.registerStatusCallback(mCallback); + } catch (RemoteException e) { + e.printStackTrace(); + } + + } + } + }; + + String getMyOwnIP() throws UnknownHostException, IOException, RemoteException, + IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException + { + String resp=""; + Socket client = new Socket(); + // Setting Keep Alive forces creation of the underlying socket, otherwise getFD returns -1 + client.setKeepAlive(true); + + + client.connect(new InetSocketAddress("v4address.com", 23),20000); + client.shutdownOutput(); + BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); + while (true) { + String line = in.readLine(); + if( line == null) + return resp; + resp+=line; + } + + } + + + + @Override + public boolean handleMessage(Message msg) { + if(msg.what == MSG_UPDATE_STATE) { + mStatus.setText((CharSequence) msg.obj); + } else if (msg.what == MSG_UPDATE_MYIP) { + + mMyIp.setText((CharSequence) msg.obj); + } + return true; + } +}
\ No newline at end of file diff --git a/remoteExample/src/main/res/drawable-hdpi/ic_launcher.png b/remoteExample/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..d4e4bdf6 --- /dev/null +++ b/remoteExample/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/remoteExample/src/main/res/drawable-mdpi/ic_launcher.png b/remoteExample/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..930cd9e7 --- /dev/null +++ b/remoteExample/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/remoteExample/src/main/res/drawable-xhdpi/ic_launcher.png b/remoteExample/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..1fa27b61 --- /dev/null +++ b/remoteExample/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/remoteExample/src/main/res/layout/activity_main.xml b/remoteExample/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..42dd4dfb --- /dev/null +++ b/remoteExample/src/main/res/layout/activity_main.xml @@ -0,0 +1,7 @@ +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="de.blinkt.openvpn.remote.MainActivity" + tools:ignore="MergeRootFrame" /> diff --git a/remoteExample/src/main/res/layout/fragment_main.xml b/remoteExample/src/main/res/layout/fragment_main.xml new file mode 100644 index 00000000..e4fa019d --- /dev/null +++ b/remoteExample/src/main/res/layout/fragment_main.xml @@ -0,0 +1,70 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context="de.blinkt.openvpn.remote.MainFragment"> + + + <TextView + android:id="@+id/status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignLeft="@+id/helloworld" + android:layout_alignParentTop="true" + android:text="@string/no_status_yet" /> + + <Button + android:id="@+id/startVPN" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignRight="@+id/helloworld" + android:layout_below="@+id/helloworld" + android:text="@string/no_now" + android:visibility="visible" /> + + <TextView + android:id="@+id/helloworld" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:text="@string/hello_world" /> + + <TextView + android:id="@+id/MyIpText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_above="@+id/helloworld" + android:layout_alignParentLeft="true" + tools:text="TextView" /> + + <Button + android:id="@+id/getMyIP" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/startVPN" + android:text="@string/show_my_ip" /> + + <Button + android:id="@+id/disconnect" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/startVPN" + android:layout_toRightOf="@+id/startVPN" + android:text="@string/disconnect" /> + + <Button + android:id="@+id/startembedded" + style="?android:attr/buttonStyleSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/getMyIP" + android:text="@string/start_embedded" /> + +</RelativeLayout> diff --git a/remoteExample/src/main/res/menu/main.xml b/remoteExample/src/main/res/menu/main.xml new file mode 100644 index 00000000..f732c1b4 --- /dev/null +++ b/remoteExample/src/main/res/menu/main.xml @@ -0,0 +1,9 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:context="de.blinkt.openvpn.remote.MainActivity" > + + <item android:id="@+id/action_settings" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> +</menu> diff --git a/remoteExample/src/main/res/values-w820dp/dimens.xml b/remoteExample/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 00000000..63fc8164 --- /dev/null +++ b/remoteExample/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ +<resources> + <!-- Example customization of dimensions originally defined in res/values/dimens.xml + (such as screen margins) for screens with more than 820dp of available width. This + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> + <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/remoteExample/src/main/res/values/dimens.xml b/remoteExample/src/main/res/values/dimens.xml new file mode 100644 index 00000000..a0171a70 --- /dev/null +++ b/remoteExample/src/main/res/values/dimens.xml @@ -0,0 +1,6 @@ +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> + + </resources> diff --git a/remoteExample/src/main/res/values/strings.xml b/remoteExample/src/main/res/values/strings.xml new file mode 100644 index 00000000..bbaee226 --- /dev/null +++ b/remoteExample/src/main/res/values/strings.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">OpenVPN Remote</string> + <string name="hello_world">Hello world!</string> + <string name="action_settings">Settings</string> + <string name="permission_description">Allows another app to control OpenVPN</string> + <string name="no_status_yet">No Status yet</string> + <string name="no_now">Not now</string> + <string name="show_my_ip">Show my IP</string> + <string name="disconnect">Disconnect</string> + <string name="start_embedded">Start embedded profile</string> + + +</resources> diff --git a/remoteExample/src/main/res/values/styles.xml b/remoteExample/src/main/res/values/styles.xml new file mode 100644 index 00000000..ff6c9d2c --- /dev/null +++ b/remoteExample/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ +<resources> + + <!-- Base application theme. --> + <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar"> + <!-- Customize your theme here. --> + </style> + +</resources> |