From 302f41085f460e67934166a3da55fa746eeaf7e2 Mon Sep 17 00:00:00 2001
From: akuma <n3k0@pc.localdomain>
Date: Fri, 9 Aug 2019 00:00:36 +0200
Subject: [PATCH] Select Apps option

---
 .../bitmaskclient/DrawerSettingsAdapter.java  |   1 +
 .../drawer/NavigationDrawerFragment.java      |   8 +
 .../se/leap/bitmaskclient/eip/Gateway.java    |  12 +-
 .../bitmaskclient/eip/GatewaysManager.java    |   2 +-
 .../fragments/Settings_Allowed_Apps.java      | 364 ++++++++++++++++++
 .../drawable-hdpi/ic_search_white_24dp.png    | Bin 0 -> 396 bytes
 .../drawable-mdpi/ic_search_white_24dp.png    | Bin 0 -> 247 bytes
 .../drawable-xhdpi/ic_search_white_24dp.png   | Bin 0 -> 465 bytes
 .../main/res/drawable-xxhdpi/bg_switchbar.xml |   9 +
 .../drawable-xxhdpi/ic_search_white_24dp.png  | Bin 0 -> 728 bytes
 .../drawable-xxxhdpi/ic_search_white_24dp.png | Bin 0 -> 915 bytes
 .../res/layout/allowed_application_layout.xml |  61 +++
 app/src/main/res/layout/allowed_vpn_apps.xml  |  75 ++++
 app/src/main/res/layout/s_layout.xml          |  12 +
 app/src/main/res/menu/allowed_apps.xml        |  15 +
 15 files changed, 557 insertions(+), 2 deletions(-)
 create mode 100644 app/src/main/java/se/leap/bitmaskclient/fragments/Settings_Allowed_Apps.java
 create mode 100755 app/src/main/res/drawable-hdpi/ic_search_white_24dp.png
 create mode 100755 app/src/main/res/drawable-mdpi/ic_search_white_24dp.png
 create mode 100755 app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png
 create mode 100644 app/src/main/res/drawable-xxhdpi/bg_switchbar.xml
 create mode 100755 app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png
 create mode 100755 app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png
 create mode 100644 app/src/main/res/layout/allowed_application_layout.xml
 create mode 100644 app/src/main/res/layout/allowed_vpn_apps.xml
 create mode 100644 app/src/main/res/layout/s_layout.xml
 create mode 100644 app/src/main/res/menu/allowed_apps.xml

diff --git a/app/src/main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java b/app/src/main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java
index 89aeb4be..024bfaba 100644
--- a/app/src/main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java
+++ b/app/src/main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java
@@ -45,6 +45,7 @@ public class DrawerSettingsAdapter extends BaseAdapter {
     public static final int BATTERY_SAVER = 3;
     public static final int ALWAYS_ON = 4;
     public static final int DONATE = 5;
+    public static final int SELECT_APPS = 6;
 
     //view types
     public final static int VIEW_SIMPLE_TEXT = 0;
diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
index 640c143a..21bc8360 100644
--- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
@@ -59,6 +59,8 @@ import se.leap.bitmaskclient.R;
 import se.leap.bitmaskclient.fragments.AboutFragment;
 import se.leap.bitmaskclient.fragments.AlwaysOnDialog;
 import se.leap.bitmaskclient.fragments.LogFragment;
+import se.leap.bitmaskclient.fragments.SelectAppsFragment;
+import se.leap.bitmaskclient.fragments.Settings_Allowed_Apps;
 
 import static android.content.Context.MODE_PRIVATE;
 import static se.leap.bitmaskclient.BitmaskApp.getRefWatcher;
@@ -74,6 +76,7 @@ import static se.leap.bitmaskclient.DrawerSettingsAdapter.DONATE;
 import static se.leap.bitmaskclient.DrawerSettingsAdapter.DrawerSettingsItem.getSimpleTextInstance;
 import static se.leap.bitmaskclient.DrawerSettingsAdapter.DrawerSettingsItem.getSwitchInstance;
 import static se.leap.bitmaskclient.DrawerSettingsAdapter.LOG;
+import static se.leap.bitmaskclient.DrawerSettingsAdapter.SELECT_APPS;
 import static se.leap.bitmaskclient.DrawerSettingsAdapter.SWITCH_PROVIDER;
 import static se.leap.bitmaskclient.R.string.about_fragment_title;
 import static se.leap.bitmaskclient.R.string.donate_title;
@@ -273,6 +276,7 @@ public class NavigationDrawerFragment extends Fragment {
             settingsListAdapter.addItem(getSimpleTextInstance(getContext(), getString(donate_title), R.drawable.ic_donate_36, DONATE));
         }
         settingsListAdapter.addItem(getSimpleTextInstance(getContext(), getString(about_fragment_title), R.drawable.ic_about_36, ABOUT));
+        settingsListAdapter.addItem(getSimpleTextInstance(getContext(), getString(about_fragment_title), R.drawable.ic_about_36, SELECT_APPS));
     }
 
     private ActionBar setupActionBar() {
@@ -494,6 +498,10 @@ public class NavigationDrawerFragment extends Fragment {
                     Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(DONATION_URL));
                     startActivity(browserIntent);
                     break;
+                case SELECT_APPS:
+                    fragment = new Settings_Allowed_Apps();
+                    setActionBarTitle(about_fragment_title);
+                    break;
                 default:
                     break;
             }
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index 55ade1ae..77666378 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -16,6 +16,9 @@
  */
 package se.leap.bitmaskclient.eip;
 
+import android.content.Context;
+import android.content.SharedPreferences;
+
 import com.google.gson.Gson;
 
 import org.json.JSONException;
@@ -23,9 +26,12 @@ import org.json.JSONObject;
 
 import java.io.IOException;
 import java.io.StringReader;
+import java.util.HashSet;
+import java.util.Set;
 
 import de.blinkt.openvpn.VpnProfile;
 import de.blinkt.openvpn.core.ConfigParser;
+import se.leap.bitmaskclient.BitmaskApp;
 
 /**
  * Gateway provides objects defining gateways and their metadata.
@@ -52,7 +58,7 @@ public class Gateway {
      * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
      * and create a VpnProfile belonging to it.
      */
-    public Gateway(JSONObject eip_definition, JSONObject secrets, JSONObject gateway) {
+    public Gateway(JSONObject eip_definition, JSONObject secrets, JSONObject gateway, Context context) {
 
         this.gateway = gateway;
         this.secrets = secrets;
@@ -62,7 +68,11 @@ public class Gateway {
         mName = locationAsName(eip_definition);
 
         mVpnProfile = createVPNProfile();
+        System.out.println("###########" + mName + "###########");
         mVpnProfile.mName = mName;
+
+        SharedPreferences allow_apps = context.getSharedPreferences("BITMASK", Context.MODE_MULTI_PROCESS);
+        mVpnProfile.mAllowedAppsVpn = new HashSet<String>(allow_apps.getStringSet("ALLOW_APPS", new HashSet<String>()));
     }
 
     private JSONObject getGeneralConfiguration(JSONObject eip_definition) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
index 060843fd..8edf81ef 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -93,7 +93,7 @@ public class GatewaysManager {
                 JSONObject gw = gatewaysDefined.getJSONObject(i);
                 if (isOpenVpnGateway(gw)) {
                     JSONObject secrets = secretsConfiguration();
-                    Gateway aux = new Gateway(eipDefinition, secrets, gw);
+                    Gateway aux = new Gateway(eipDefinition, secrets, gw, context);
                     if (!gateways.contains(aux)) {
                         addGateway(aux);
                     }
diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/Settings_Allowed_Apps.java b/app/src/main/java/se/leap/bitmaskclient/fragments/Settings_Allowed_Apps.java
new file mode 100644
index 00000000..f930b278
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/fragments/Settings_Allowed_Apps.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2012-2016 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package se.leap.bitmaskclient.fragments;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.support.v4.app.Fragment;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.CompoundButton;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.SearchView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.Vector;
+
+import de.blinkt.openvpn.VpnProfile;
+import se.leap.bitmaskclient.EipSetupListener;
+import se.leap.bitmaskclient.EipSetupObserver;
+import se.leap.bitmaskclient.R;
+
+/**
+ * Created by arne on 16.11.14.
+ */
+public class Settings_Allowed_Apps extends Fragment implements AdapterView.OnItemClickListener, CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+    private ListView mListView;
+    private VpnProfile mProfile;
+    private TextView mDefaultAllowTextView;
+    private PackageAdapter mListAdapter;
+
+    private SharedPreferences allow_apps;
+    private SharedPreferences.Editor allow_apps_editor;
+    private Set<String> apps;
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        AppViewHolder avh = (AppViewHolder) view.getTag();
+        avh.checkBox.toggle();
+    }
+
+    @Override
+    public void onClick(View v) {
+
+    }
+
+    static class AppViewHolder {
+        public ApplicationInfo mInfo;
+        public View rootView;
+        public TextView appName;
+        public ImageView appIcon;
+        //public TextView appSize;
+        //public TextView disabled;
+        public CompoundButton checkBox;
+
+        static public AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = inflater.inflate(R.layout.allowed_application_layout, parent, false);
+
+                // Creates a ViewHolder and store references to the two children views
+                // we want to bind data to.
+                AppViewHolder holder = new AppViewHolder();
+                holder.rootView = convertView;
+                holder.appName = (TextView) convertView.findViewById(R.id.app_name);
+                holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
+                //holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
+                //holder.disabled = (TextView) convertView.findViewById(R.id.app_disabled);
+                holder.checkBox = (CompoundButton) convertView.findViewById(R.id.app_selected);
+                convertView.setTag(holder);
+
+                return holder;
+            } else {
+                // Get the ViewHolder back to get fast access to the TextView
+                // and the ImageView.
+                return (AppViewHolder) convertView.getTag();
+            }
+        }
+
+    }
+
+    @Override
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        String packageName = (String) buttonView.getTag();
+
+        if (isChecked) {
+            Log.d("openvpn", "adding to allowed apps" + packageName);
+
+            apps.add(packageName);
+
+        } else {
+            Log.d("openvpn", "removing from allowed apps" + packageName);
+            //Log.d("eneko", this.getContext().getSharedPreferences("BITMASK", Context.MODE_MULTI_PROCESS).getString("ALLOW_APPS", ""));
+
+            apps.remove(packageName);
+        }
+        allow_apps_editor.clear();
+        allow_apps_editor.putStringSet("ALLOW_APPS", apps);
+        allow_apps_editor.apply();
+    }
+
+
+    class PackageAdapter extends BaseAdapter implements Filterable {
+        private Vector<ApplicationInfo> mPackages;
+        private final LayoutInflater mInflater;
+        private final PackageManager mPm;
+        private ItemFilter mFilter = new ItemFilter();
+        private Vector<ApplicationInfo> mFilteredData;
+
+
+        private class ItemFilter extends Filter {
+            @Override
+            protected FilterResults performFiltering(CharSequence constraint) {
+
+                String filterString = constraint.toString().toLowerCase(Locale.getDefault());
+
+                FilterResults results = new FilterResults();
+
+
+                int count = mPackages.size();
+                final Vector<ApplicationInfo> nlist = new Vector<>(count);
+
+                for (int i = 0; i < count; i++) {
+                    ApplicationInfo pInfo = mPackages.get(i);
+                    CharSequence appName = pInfo.loadLabel(mPm);
+
+                    if (TextUtils.isEmpty(appName))
+                        appName = pInfo.packageName;
+
+                    if (appName instanceof  String) {
+                        if (((String) appName).toLowerCase(Locale.getDefault()).contains(filterString))
+                                nlist.add(pInfo);
+                    } else {
+                        if (appName.toString().toLowerCase(Locale.getDefault()).contains(filterString))
+                            nlist.add(pInfo);
+                    }
+                }
+                results.values = nlist;
+                results.count = nlist.size();
+
+                return results;
+            }
+
+            @Override
+            protected void publishResults(CharSequence constraint, FilterResults results) {
+                mFilteredData = (Vector<ApplicationInfo>) results.values;
+                notifyDataSetChanged();
+            }
+
+        }
+
+
+        PackageAdapter(Context c, VpnProfile vp) {
+            mPm = c.getPackageManager();
+            mProfile = vp;
+            mInflater = LayoutInflater.from(c);
+
+            mPackages = new Vector<>();
+            mFilteredData = mPackages;
+        }
+
+        private void populateList(Activity c) {
+            List<ApplicationInfo> installedPackages = mPm.getInstalledApplications(PackageManager.GET_META_DATA);
+
+            // Remove apps not using Internet
+
+            int androidSystemUid = 0;
+            ApplicationInfo system = null;
+            Vector<ApplicationInfo> apps = new Vector<ApplicationInfo>();
+
+            try {
+                system = mPm.getApplicationInfo("android", PackageManager.GET_META_DATA);
+                androidSystemUid = system.uid;
+                apps.add(system);
+            } catch (PackageManager.NameNotFoundException e) {
+            }
+
+
+            for (ApplicationInfo app : installedPackages) {
+
+                if (mPm.checkPermission(Manifest.permission.INTERNET, app.packageName) == PackageManager.PERMISSION_GRANTED &&
+                        app.uid != androidSystemUid) {
+
+                    apps.add(app);
+                }
+            }
+
+            Collections.sort(apps, new ApplicationInfo.DisplayNameComparator(mPm));
+            mPackages = apps;
+            mFilteredData = apps;
+            c.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    notifyDataSetChanged();
+                }
+            });
+        }
+
+        @Override
+        public int getCount() {
+            return mFilteredData.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mFilteredData.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return mFilteredData.get(position).packageName.hashCode();
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            AppViewHolder viewHolder = AppViewHolder.createOrRecycle(mInflater, convertView, parent);
+            convertView = viewHolder.rootView;
+            viewHolder.mInfo = mFilteredData.get(position);
+            final ApplicationInfo mInfo = mFilteredData.get(position);
+
+
+            CharSequence appName = mInfo.loadLabel(mPm);
+
+            if (TextUtils.isEmpty(appName))
+                appName = mInfo.packageName;
+            viewHolder.appName.setText(appName);
+            viewHolder.appIcon.setImageDrawable(mInfo.loadIcon(mPm));
+            viewHolder.checkBox.setTag(mInfo.packageName);
+            viewHolder.checkBox.setOnCheckedChangeListener(Settings_Allowed_Apps.this);
+            viewHolder.checkBox.setChecked(apps.contains(mInfo.packageName));
+
+
+    //            viewHolder.checkBox.setChecked(mProfile.mAllowedAppsVpn.contains(mInfo.packageName));
+            return viewHolder.rootView;
+        }
+
+        @Override
+        public Filter getFilter() {
+            return mFilter;
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+//        changeDisallowText(mProfile.mAllowedAppsVpnAreDisallowed);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        allow_apps = this.getContext().getSharedPreferences("BITMASK", Context.MODE_MULTI_PROCESS);
+        allow_apps_editor = allow_apps.edit();
+        apps = new HashSet<String>(allow_apps.getStringSet("ALLOW_APPS", new HashSet<String>()));
+
+//        /String profileUuid = getArguments().getString(getActivity().getPackageName() + ".profileUUID");
+//        mProfile = EipSetupObserver.getProfile();
+//        getActivity().setTitle(getString(R.string.edit_profile_title, mProfile.getName()));
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.allowed_apps, menu);
+
+        SearchView searchView = (SearchView) menu.findItem( R.id.app_search_widget ).getActionView();
+        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+            @Override
+            public boolean onQueryTextSubmit(String query) {
+                mListView.setFilterText(query);
+                mListView.setTextFilterEnabled(true);
+                return true;
+            }
+
+            @Override
+            public boolean onQueryTextChange(String newText) {
+                mListView.setFilterText(newText);
+                if (TextUtils.isEmpty(newText))
+                    mListView.setTextFilterEnabled(false);
+                else
+                    mListView.setTextFilterEnabled(true);
+
+                return true;
+            }
+        });
+        searchView.setOnCloseListener(new SearchView.OnCloseListener() {
+            @Override
+            public boolean onClose() {
+                mListView.clearTextFilter();
+                mListAdapter.getFilter().filter("");
+                return false;
+            }
+        });
+
+        super.onCreateOptionsMenu(menu, inflater);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        View v = inflater.inflate(R.layout.allowed_vpn_apps, container, false);
+
+        mDefaultAllowTextView = (TextView) v.findViewById(R.id.default_allow_text);
+
+        Switch vpnOnDefaultSwitch = (Switch) v.findViewById(R.id.default_allow);
+
+        vpnOnDefaultSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+
+                changeDisallowText(isChecked);
+                mProfile.mAllowedAppsVpnAreDisallowed = isChecked;
+            }
+        });
+
+//        vpnOnDefaultSwitch.setChecked(mProfile.mAllowedAppsVpnAreDisallowed);
+
+        mListView = (ListView) v.findViewById(android.R.id.list);
+
+        mListAdapter = new PackageAdapter(getActivity(), mProfile);
+        mListView.setAdapter(mListAdapter);
+        mListView.setOnItemClickListener(this);
+
+        mListView.setEmptyView(v.findViewById(R.id.loading_container));
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                mListAdapter.populateList(getActivity());
+            }
+        }).start();
+
+        return v;
+    }
+
+    private void changeDisallowText(boolean selectedAreDisallowed) {
+        if (selectedAreDisallowed)
+            mDefaultAllowTextView.setText(R.string.vpn_disallow_radio);
+        else
+            mDefaultAllowTextView.setText(R.string.vpn_allow_radio);
+    }
+}
diff --git a/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png
new file mode 100755
index 0000000000000000000000000000000000000000..bbfbc96cbce0dc0bf2a0b5ac4fa924dab5e34919
GIT binary patch
literal 396
zcmeAS@N?(olHy`uVBq!ia0y~yU{C>J4i*Lm25-&)VFm`qU{4pvkP61Pmlt~J1j?{|
zSgdnq*<SN*2_3Qd4k_Jd*R5A*W1W1om%FSvVb%-on9|d!wd@Bs`3kU2PHZ~y=)KtG
zqPG7#XaC)2R`bWA_`#C-Yow(6=15y_U9NugY^{yKXVwOD;pYFXzp{4jk<x1a@|RV|
zW|vW5HDis1PJ7mYwYo2NXZM*d*c!|x%b+*S-R$haH9WH#Z@($Hu70C`A#Z%djaM`7
zHN2LvV(O1|cK#sZ{QSWxVW|eQwqpsGIKLiHYV&iv%bDA(*7of{_tw6boHLFEoR_wE
z_hVzaLD#|*CaKvoAF5V-kLLOLRP@YSy}8eX3c`L}YqLABT-ur0_O_o5^RsK-3!emN
z&$pbh>ePai<7bNQ&5dNYd9rd%-u<oWYg><9dGWhzc|&z51MA!L6;l^1`NXgzxHZ7x
z?(>x?rLX$6T9{t(8T2ijp=BTF(A#Vswbef0PJPlg{Wsn7m^~R77#KWV{an^LB{Ts5
D(W<c}

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-mdpi/ic_search_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_search_white_24dp.png
new file mode 100755
index 0000000000000000000000000000000000000000..faefc59c8e574774fda7a43bcb2214bd109eaa1c
GIT binary patch
literal 247
zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4i*Lm2CurW#S9D#`#fD7Lo5W(9<=T{lpw)y
zfd8!JzXj<LH{Ak0gmv9hZr%ICV$#K+U}^2tZNf|^CtaG^@jmV0l22ilvyT5y**1$S
zvSjno!$C$9qE`mGh;!TgInv%KwK?N?OR7o4V}Z9J9v2f@qPKiLrXa=hv8zn$$;E`8
z>Mg}bw4`!2^p`zb;rr<Ddi7-&T^n!RI&tB~G2z`W)Pgf^J=E&YOXJInd-rXb;JO_r
z7VGci_R{<{NjuQ%;`Er#%_6m)Ug?^@CNKFBSO5RPb<c0JgUuNj7#KWV{an^LB{Ts5
DfXr@#

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png
new file mode 100755
index 0000000000000000000000000000000000000000..bfc3e39394246b221f7d4617aa5600a6406aa7c7
GIT binary patch
literal 465
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4i*LmhQHi~JPZtsGdx`!Ln1ie9`yH$4wPtr
zc;Cg@gzwFb&iZA&76KC+9$AUpJr=sy?QWCs(cBKDt1er3&#69OGRgkHReAf<*8&G2
z{^#4$15SMQaCY|Gc)(D1*OMnDnMFtT{1d(Q!_S_7&8vLFW0vzR7q5Br<B+mqu39Kh
zvdyC}I}+w*x=EzA@#=lNH6@hgxrM*ZE9Pl09?a2_%eJ^L5iTFH<7i}{PF=!0iR%m@
zFB0B~wzK;5yVf?P&RLaI(6cjuA$*4O(FLuM;S959NFQC$Ju{49^9=E$3degi8ibzt
z8lEs*%(bAUGMs1E3`tgl_R3J6Su=Q9H?(GIvANIiW-aK=)L{E;;r)xnbk6#&!_3Q^
zU6~CI8b9;f7;$vx(nWSN7PP($47NE}V5Re#Im_npoE03WwtQT7BW0S9>a!b<r1aCh
zjv9E*o*+}aKuM$cRD!MFj6*lW4c8R-+m~PY!hLUE*P>I3=6gOIvtDnK8E{qJ<ijhk
zM&>=cf)eEO!!<XwZ(l63;&HAv>#K^HK?&me!CY@1q<A&w)}5ZhCpojN?Vr0nBk#FC
WhfPINcQP<AFnGH9xvX<aXaWE!=*I^D

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xxhdpi/bg_switchbar.xml b/app/src/main/res/drawable-xxhdpi/bg_switchbar.xml
new file mode 100644
index 00000000..7af57ad3
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/bg_switchbar.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2012-2016 Arne Schwabe
+  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/switchbar" />
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png
new file mode 100755
index 0000000000000000000000000000000000000000..abbb9895108b56aedde011bdde5eb8ddaff78838
GIT binary patch
literal 728
zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84i*LmhW}5h&oD4B&G&S149U3ncE(vR=|G9L
z{*;MZR5Guuy{PA0%;>ssb=O`7Lq~lF7e|gSb}aQl0;@%Lg?(gFY2uE4rBo96|HE{>
zRjy1zi8Z@c<es0o<mH@)-bSsJ(+^kfvzash{TY{)28oASI3L@WOiphUN)IVmQQ*~C
zSyZys`I+eKJAwA)IR;m}W{TH5y8rWlgLaDC#*XWEf>o}?%(b&io^G`F?B&X1mftK@
zFR!_|^6JlHN|r`PItA37=c%rHrLpvBsoZs)YnG2)Uib7E9dtc=$n*WuS!@4Hb7{Wl
z85aAv`x5(I8{5lrGnl@!UDnr3`8GGRKjh$oGS%IiHb#go*;&vW<TU$Om|w&M{~cFu
zm`cvgdwfo6`x152ZC=Y;0`9%A4)nG<w)GglH-~Npt9b2)(^B^8iXoS{p4EI<EVW-%
zv1FO(mqP7{SAPg|F6;_3c-QQo@u#<C!eY^$9}kqJ-m@gOO_HkDRxFX)V!LGirak<1
zK99GnwmjR~<Zbsuw0Hhgfm4~R>uXA0y_d3AQoNFSCslabmnntQmrilHaiJ@2!WM<#
znyWWTRfXGnw$CaTds5w8vvSEH_r(PVmA~FvP&Oq-qF}GQxXsHKJ%@``CGYLnrxYgr
zRyAnd(I!b%>7*&K_Jt=?_O91W@{x>|OgO(Swj%JAsouo-JJV(cZc^!(pmJJ5+wJ~O
z<N2~)b}6qkZa$e|_cKx}+Ut$vd?)u$UWQ%AUS^84OD-yrIL~)<>Xb$EWBIMD_w(&~
z{WCH6pJCDj(^d6>D>tnxPwk6dv_xbskMVIa^*av=PIKNqWAtdBQ{^oq)$^}vWp+;C
zYg#cSZ~LXh6;Bp!+%@&v?e!%Ai?0Z4?VNJyfAgj3-?ZPdd!1N%rA~I~lts@SH6^U}
nPi<QKd4Y(^TyVlko-1E&(py%sQ6rdvfq}u()z4*}Q$iB}t+rIG

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png
new file mode 100755
index 0000000000000000000000000000000000000000..dd5adfc7f990333743a5f3b34f09098c1377a4a4
GIT binary patch
literal 915
zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4i*Lm29JsRH#0CWM|rw9hEy=VJ$N=dB%I;c
zL-W}_yLfMUAAHH9SMsQ3f$ZJuA$LjzOO$n;@A&WAze`6{smm~J{fl^osR;tnsuA7W
zZksJ$#BHk5^uXn?Y+dd`*7GMN&z{-)JWcP;^NB$#L$s!PEe+CDIxoPj6P!9%cU#2r
zQ@UajO3S|;b3FHPPJiVw8P=`xRu%`jtV{bJXQ_Yr+>my6x&PY+y=N;gcUg(Qxb#lx
z`1_WR9c#+?-79?~q^E4L{xQpUQNUyEqy39>E}e)!W9-qGXZ3Q)Y^LUzq^WLMH7hG7
zJ~q{347XUqW^pEzDaP!CmR-#;`5ku>*4s78m={)hxD_VOdwZ`a%KAw8l^gH&JaV)z
z&C!1mDy;EID0*)5r+z-UglfkyU!~*PIohB4g>T%uv|8-KakXuXKaX*3yPPGMnp6H%
zeD94s4VGye-cI+Qb68|UxZ(u$10`X0A7w6!wjMF{@qfx+nbR&Jq?!}?^N_$Ydr`(!
zGfsVKW%c>o#W3A4c8b1EvQ!9*MQ5hxzJQ}40W2CxQB(ETB)<w{d2w)yTKtVeS5|UN
zIQS)Q--gze!Ga3SniF8MU)*3iR-F>hO9YwH`f^)MBI9LM*9KRg?oX|XKC8KU`Q(zd
zPKy`a;L}#=OE`U6eqQ3PRTFmfz3i+>;tM_?&ed3Z#qjJ&b*>wAK^!{C)(ZP~w7-qs
z+F^X--3jx)<kf*8+%KNXZcF~u|NBPVq~OUXHn+X)d*mQrnzQ~zu<(m?-e`yQj}EeK
zyS-$M>?5A{H{P)yZPk-|xXC4M{sFsbg+HhI9c|Z<-oX;HcnO=uq0(17)@*kFxX7i~
zZB@)O71yd06Bf2BEsuC&*vne8ct>H6)WzAMH5wZoBcxeoq>0FHvQg=||6`70j&T>i
z<hkJGqKx*FF52J7iz-}ng6r(B)<yrCPj7j2l%f3Q%K4HP*b8O{-MMi-a^5FH{~3=P
zbZYu<FDYY=JQb7D8G5b4|A6$WJ82V*DoqdQ<W2VCN-LZl5M<DNyJb>S=AC0xMSLEc
zEnAYnwcSvaHF!s_mgbBjxgnkhS@M*<xYP=y!-5R@Zf}^>RCq_ri|bgyY7I?^qe~Y(
zNf3#4J$Z;jew9Y0#KU?~RpG;bRxf(8$jKF&;8%vs{w=L^((&<5_h<<Q1_lOCS3j3^
HP6<r_zZIYc

literal 0
HcmV?d00001

diff --git a/app/src/main/res/layout/allowed_application_layout.xml b/app/src/main/res/layout/allowed_application_layout.xml
new file mode 100644
index 00000000..d8d846f9
--- /dev/null
+++ b/app/src/main/res/layout/allowed_application_layout.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2012-2016 Arne Schwabe
+  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+  -->
+
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingTop="8dip"
+    android:paddingBottom="8dip"
+    android:columnCount="4"
+    tools:ignore="RtlCompat">
+
+    <ImageView
+        android:id="@+id/app_icon"
+        android:layout_width="@android:dimen/app_icon_size"
+        android:layout_height="@android:dimen/app_icon_size"
+        android:layout_rowSpan="1"
+        android:layout_marginEnd="8dip"
+        android:scaleType="centerInside"
+        tools:background="@drawable/icon"
+        android:contentDescription="@null" />
+
+    <TextView
+        tools:text="@string/app"
+        android:id="@+id/app_name"
+        android:layout_width="0dip"
+        android:layout_columnSpan="2"
+        android:layout_rowSpan="1"
+        android:layout_gravity="fill_horizontal|center_vertical"
+        android:layout_marginTop="2dip"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textAlignment="viewStart" />
+
+    <CheckBox android:id="@+id/app_selected"
+        android:layout_marginStart="8dip"
+        android:layout_gravity="center_vertical"
+        android:layout_rowSpan="1"
+        android:visibility="visible" />
+
+<!--    <TextView
+        android:id="@+id/app_size"
+        android:layout_width="0dip"
+        android:layout_gravity="fill_horizontal|top"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAlignment="viewStart" /> -->
+
+    <!-- <TextView
+        android:id="@+id/app_disabled"
+        android:layout_marginStart="8dip"
+        android:layout_gravity="top"
+        android:textAppearance="?android:attr/textAppearanceSmall" /> -->
+
+</GridLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/allowed_vpn_apps.xml b/app/src/main/res/layout/allowed_vpn_apps.xml
new file mode 100644
index 00000000..0883f4b8
--- /dev/null
+++ b/app/src/main/res/layout/allowed_vpn_apps.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (c) 2012-2016 Arne Schwabe
+  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    tools:ignore="RtlCompat"
+    android:layout_height="match_parent">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/actionBarSize"
+        android:background="@drawable/bg_switchbar"
+        android:paddingStart="@dimen/switchbar_pad"
+        android:elevation="1dp"
+        android:paddingEnd="@dimen/switchbar_pad"
+        tools:ignore="NewApi">
+
+        <Switch
+            android:id="@+id/default_allow"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+             />
+        <TextView
+            android:id="@+id/default_allow_text"
+            style="@android:style/TextAppearance.Medium.Inverse"
+            tools:text="@string/vpn_disallow_radio"
+            android:layout_toStartOf="@id/default_allow"
+            android:layout_width="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_height="wrap_content" />
+        </RelativeLayout>
+
+    <ListView
+        android:visibility="gone"
+        android:id="@android:id/list"
+        android:drawSelectorOnTop="false"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipToPadding="false"
+        android:scrollbarStyle="outsideOverlay" />
+
+
+
+    <LinearLayout
+        android:id="@+id/loading_container"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="visible"
+        android:gravity="center">
+
+        <ProgressBar
+            style="?android:attr/progressBarStyleLarge"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:text="@string/loading"
+            android:paddingTop="4dip"
+            android:singleLine="true" />
+
+    </LinearLayout>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/s_layout.xml b/app/src/main/res/layout/s_layout.xml
new file mode 100644
index 00000000..d5717a5b
--- /dev/null
+++ b/app/src/main/res/layout/s_layout.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/buttontest"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="A"/>
+
+</ScrollView>
diff --git a/app/src/main/res/menu/allowed_apps.xml b/app/src/main/res/menu/allowed_apps.xml
new file mode 100644
index 00000000..20eb91e1
--- /dev/null
+++ b/app/src/main/res/menu/allowed_apps.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2012-2015 Arne Schwabe
+  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item
+        android:id="@+id/app_search_widget"
+        app:actionViewClass="android.widget.SearchView"
+        android:icon="@drawable/ic_search_white_24dp"
+        app:showAsAction="always"
+        android:title="@string/Search"/>
+</menu>
\ No newline at end of file
-- 
GitLab