App Filter

This commit is contained in:
Rsplwe 2019-03-24 14:07:08 +08:00
parent 1da2f456b8
commit ca34f79307
12 changed files with 360 additions and 8 deletions

View File

@ -44,6 +44,7 @@ dependencies {
implementation 'androidx.percentlayout:percentlayout:1.0.0' implementation 'androidx.percentlayout:percentlayout:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha03'
//DNS //DNS
implementation 'org.pcap4j:pcap4j-core:1.7.4' implementation 'org.pcap4j:pcap4j-core:1.7.4'
implementation 'org.pcap4j:pcap4j-packetfactory-static:1.7.4' implementation 'org.pcap4j:pcap4j-packetfactory-static:1.7.4'
@ -51,8 +52,8 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.okhttp3:okhttp:3.12.1' implementation 'com.squareup.okhttp3:okhttp:3.12.1'
//Analytics //Analytics
implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.google.firebase:firebase-core:16.0.8'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8' implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'

View File

@ -71,6 +71,7 @@
android:configChanges="keyboard|keyboardHidden|screenLayout|uiMode|orientation|screenSize|smallestScreenSize" android:configChanges="keyboard|keyboardHidden|screenLayout|uiMode|orientation|screenSize|smallestScreenSize"
android:theme="@style/AppTheme.NoActionBar"> android:theme="@style/AppTheme.NoActionBar">
</activity> </activity>
<activity android:name=".activity.FilterAppProxyActivity" />
</application> </application>
</manifest> </manifest>

View File

@ -0,0 +1,195 @@
package org.itxtech.daedalus.activity;
import android.annotation.SuppressLint;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.R;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* Daedalus Project
*
* @author iTX Technologies
* @link https://itxtech.org
* <p>
* 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 3 of the License, or
* (at your option) any later version.
*/
public class FilterAppProxyActivity extends AppCompatActivity {
private RecyclerViewAdapter adapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
if (Daedalus.isDarkTheme()) {
setTheme(R.style.AppTheme_Dark_NoActionBar);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_filter_app);
Toolbar toolbar = findViewById(R.id.toolbar_filter);
Drawable drawable = ContextCompat.getDrawable(this, R.drawable.ic_clear);
RecyclerView recyclerView = findViewById(R.id.recyclerView_app_filter_list);
LinearLayoutManager manager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(manager);
Drawable wrappedDrawable = DrawableCompat.wrap(Objects.requireNonNull(drawable));
DrawableCompat.setTint(wrappedDrawable, Color.WHITE);
toolbar.setNavigationIcon(drawable);
toolbar.setNavigationOnClickListener(v -> onBackPressed());
adapter = new RecyclerViewAdapter(getAppList());
recyclerView.setAdapter(adapter);
}
@Override
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
Toolbar toolbar = findViewById(R.id.toolbar_filter);
toolbar.setTitle(R.string.settings_app_filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
Daedalus.configurations.save();
}
@Override
public void onResume() {
super.onResume();
adapter.notifyDataSetChanged();
}
private class AppObject {
private String app_name;
private String app_package_name;
private Drawable app_icon;
AppObject(String appName, String packageName, Drawable appIcon) {
this.app_name = appName;
this.app_package_name = packageName;
this.app_icon = appIcon;
}
}
private ArrayList<AppObject> getAppList() {
PackageManager packageManager = getBaseContext().getPackageManager();
List<PackageInfo> packages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
ArrayList<AppObject> appObjects = new ArrayList<>();
for (PackageInfo pkg : packages) {
appObjects.add(new AppObject(
pkg.applicationInfo.loadLabel(packageManager).toString(),
pkg.packageName,
pkg.applicationInfo.loadIcon(packageManager)
));
}
return appObjects;
}
private class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private ArrayList<AppObject> appList;
@SuppressLint("UseSparseArrays")
private Map<Integer, Boolean> check_status = new HashMap<>();
RecyclerViewAdapter(ArrayList<AppObject> appObjects) {
appList = appObjects;
for (int i = 0; i < appObjects.size(); i++) {
if (Daedalus.configurations.getFilterAppObjects().contains(appObjects.get(i).app_package_name)) {
check_status.put(i, true);
}
}
}
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_appview, parent, false);
return new RecyclerViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
String package_name = appList.get(position).app_package_name;
holder.app_name.setText(appList.get(position).app_name);
holder.app_icon.setImageDrawable(appList.get(position).app_icon);
holder.app_package_name = package_name;
holder.app_check.setOnCheckedChangeListener(null);
if (Daedalus.configurations.getFilterAppObjects().contains(package_name)) {
holder.app_check.setChecked(true);
//check_status.put(position, true);
}
holder.app_check.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
check_status.put(position, true);
} else {
check_status.remove(position);
}
});
if (check_status != null && check_status.containsKey(position)) {
holder.app_check.setChecked(true);
} else {
holder.app_check.setChecked(false);
}
}
@Override
public int getItemCount() {
return appList.size();
}
}
private class RecyclerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView app_icon;
private TextView app_name;
private CheckBox app_check;
private String app_package_name;
RecyclerViewHolder(@NonNull View itemView) {
super(itemView);
app_icon = itemView.findViewById(R.id.app_icon);
app_name = itemView.findViewById(R.id.app_name);
app_check = itemView.findViewById(R.id.app_check);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (app_check.isChecked()) {
app_check.setChecked(false);
Daedalus.configurations.getFilterAppObjects().remove(app_package_name);
} else {
app_check.setChecked(true);
Daedalus.configurations.getFilterAppObjects().add(app_package_name);
}
}
}
}

View File

@ -5,9 +5,12 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.*; import android.preference.*;
import android.view.View; import android.view.View;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import org.itxtech.daedalus.Daedalus; import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.R; import org.itxtech.daedalus.R;
import org.itxtech.daedalus.activity.FilterAppProxyActivity;
import org.itxtech.daedalus.activity.MainActivity; import org.itxtech.daedalus.activity.MainActivity;
import org.itxtech.daedalus.util.server.DNSServerHelper; import org.itxtech.daedalus.util.server.DNSServerHelper;
@ -82,14 +85,28 @@ public class GlobalConfigFragment extends PreferenceFragment {
SwitchPreference boot = (SwitchPreference) findPreference("settings_boot"); SwitchPreference boot = (SwitchPreference) findPreference("settings_boot");
boot.setEnabled(false); boot.setEnabled(false);
boot.setChecked(false); boot.setChecked(false);
SwitchPreference app_filter = (SwitchPreference) findPreference("settings_app_filter_switch");
app_filter.setEnabled(false);
app_filter.setChecked(false);
} }
SwitchPreference advanced = (SwitchPreference) findPreference("settings_advanced_switch"); SwitchPreference advanced = (SwitchPreference) findPreference("settings_advanced_switch");
advanced.setOnPreferenceChangeListener((preference, newValue) -> { advanced.setOnPreferenceChangeListener((preference, newValue) -> {
updateAdvancedOptions((boolean) newValue); updateOptions((boolean) newValue, "settings_advanced");
return true; return true;
}); });
SwitchPreference app_filter = (SwitchPreference) findPreference("settings_app_filter_switch");
app_filter.setOnPreferenceChangeListener((p, w) -> {
updateOptions((boolean) w, "settings_app_filter");
return true;
});
findPreference("settings_app_filter_list").setOnPreferenceClickListener(preference -> {
startActivity(new Intent(getActivity(), FilterAppProxyActivity.class));
return false;
});
findPreference("settings_check_update").setOnPreferenceClickListener(preference -> { findPreference("settings_check_update").setOnPreferenceClickListener(preference -> {
Daedalus.openUri("https://github.com/iTXTech/Daedalus/releases"); Daedalus.openUri("https://github.com/iTXTech/Daedalus/releases");
return false; return false;
@ -110,11 +127,12 @@ public class GlobalConfigFragment extends PreferenceFragment {
return false; return false;
}); });
updateAdvancedOptions(advanced.isChecked()); updateOptions(advanced.isChecked(), "settings_advanced");
updateOptions(app_filter.isChecked(), "settings_app_filter");
} }
private void updateAdvancedOptions(boolean checked) { private void updateOptions(boolean checked, String pref) {
PreferenceCategory category = (PreferenceCategory) findPreference("settings_advanced"); PreferenceCategory category = (PreferenceCategory) findPreference(pref);
for (int i = 1; i < category.getPreferenceCount(); i++) { for (int i = 1; i < category.getPreferenceCount(); i++) {
Preference preference = category.getPreference(i); Preference preference = category.getPreference(i);
if (checked) { if (checked) {

View File

@ -7,12 +7,15 @@ import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.VpnService; import android.net.VpnService;
import android.os.Build; import android.os.Build;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.system.OsConstants; import android.system.OsConstants;
import android.util.Log; import android.util.Log;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import org.itxtech.daedalus.Daedalus; import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.R; import org.itxtech.daedalus.R;
import org.itxtech.daedalus.activity.MainActivity; import org.itxtech.daedalus.activity.MainActivity;
@ -28,6 +31,7 @@ import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Set;
/** /**
* Daedalus Project * Daedalus Project
@ -238,7 +242,28 @@ public class DaedalusVpnService extends VpnService implements Runnable {
.setConfigureIntent(PendingIntent.getActivity(this, 0, .setConfigureIntent(PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class).putExtra(MainActivity.LAUNCH_FRAGMENT, MainActivity.FRAGMENT_SETTINGS), new Intent(this, MainActivity.class).putExtra(MainActivity.LAUNCH_FRAGMENT, MainActivity.FRAGMENT_SETTINGS),
PendingIntent.FLAG_ONE_SHOT)); PendingIntent.FLAG_ONE_SHOT));
//Set App Filter
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && Daedalus.getPrefs().getBoolean("settings_app_filter_switch", false)) {
Set<String> apps = Daedalus.getPrefs().getStringSet("filterAppObjects", null);
if (apps != null) {
boolean mode = Daedalus.getPrefs().getBoolean("settings_app_filter_mode_switch", false);
for (String app : apps) {
try {
if (mode) {
builder.addDisallowedApplication(app);
} else {
builder.addAllowedApplication(app);
}
} catch (PackageManager.NameNotFoundException e) {
Logger.error("Package Not Found:" + app);
}
}
}
}
String format = null; String format = null;
for (String prefix : new String[]{"10.0.0", "192.0.2", "198.51.100", "203.0.113", "192.168.50"}) { for (String prefix : new String[]{"10.0.0", "192.0.2", "198.51.100", "203.0.113", "192.168.50"}) {
try { try {
builder.addAddress(prefix + ".1", 24); builder.addAddress(prefix + ".1", 24);
@ -303,8 +328,10 @@ public class DaedalusVpnService extends VpnService implements Runnable {
Thread.sleep(1000); Thread.sleep(1000);
} }
} }
} catch (InterruptedException ignored) { } catch (
} catch (Exception e) { InterruptedException ignored) {
} catch (
Exception e) {
Logger.logException(e); Logger.logException(e);
} finally { } finally {
Log.d(TAG, "quit"); Log.d(TAG, "quit");

View File

@ -2,6 +2,7 @@ package org.itxtech.daedalus.util;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import org.itxtech.daedalus.Daedalus; import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.util.server.CustomDNSServer; import org.itxtech.daedalus.util.server.CustomDNSServer;
@ -28,6 +29,7 @@ public class Configurations {
private static File file; private static File file;
private ArrayList<CustomDNSServer> customDNSServers; private ArrayList<CustomDNSServer> customDNSServers;
private ArrayList<String> filterAppObjects;
private ArrayList<Rule> hostsRules; private ArrayList<Rule> hostsRules;
private ArrayList<Rule> dnsmasqRules; private ArrayList<Rule> dnsmasqRules;
@ -66,6 +68,13 @@ public class Configurations {
return customDNSServers; return customDNSServers;
} }
public ArrayList<String> getFilterAppObjects() {
if (filterAppObjects == null) {
filterAppObjects = new ArrayList<>();
}
return filterAppObjects;
}
public ArrayList<Rule> getHostsRules() { public ArrayList<Rule> getHostsRules() {
if (hostsRules == null) { if (hostsRules == null) {
hostsRules = new ArrayList<>(); hostsRules = new ArrayList<>();

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_filter"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:background="@color/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView_app_filter_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</androidx.recyclerview.widget.RecyclerView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/app_icon"
android:layout_width="46dp"
android:layout_height="46dp"
android:paddingEnd="10dp"
android:paddingLeft="14dp"
android:paddingRight="10dp"
android:paddingStart="14dp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/app_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0" />
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/app_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:paddingEnd="6dp"
android:paddingLeft="2dp"
android:paddingRight="6dp"
android:paddingStart="2dp" />
</LinearLayout>

View File

@ -83,6 +83,12 @@
<string name="settings_server_address">服务器地址</string> <string name="settings_server_address">服务器地址</string>
<string name="settings_server_port">服务器端口</string> <string name="settings_server_port">服务器端口</string>
<string name="settings_app_filter">分应用代理</string>
<string name="settings_app_filter_list">选择应用</string>
<string name="settings_app_filter_mode">黑名单模式</string>
<string name="settings_app_filter_list_summary">选择要过滤的应用</string>
<string name="settings_app_filter_summary">仅支持Android Lollipop及以上版本</string>
<string name="server_pdomo_primary">PdoMo DNS 主服务器</string> <string name="server_pdomo_primary">PdoMo DNS 主服务器</string>
<string name="server_pdomo_secondary">PdoMo DNS 辅服务器</string> <string name="server_pdomo_secondary">PdoMo DNS 辅服务器</string>
<string name="server_puredns_south_china">PureDNS 华南</string> <string name="server_puredns_south_china">PureDNS 华南</string>

View File

@ -83,6 +83,12 @@
<string name="settings_server_address">伺服器網址</string> <string name="settings_server_address">伺服器網址</string>
<string name="settings_server_port">伺服器埠</string> <string name="settings_server_port">伺服器埠</string>
<string name="settings_app_filter">分應用代理</string>
<string name="settings_app_filter_list">選擇應用</string>
<string name="settings_app_filter_mode">黑名單模式</string>
<string name="settings_app_filter_list_summary">選擇要過濾的應用</string>
<string name="settings_app_filter_summary">僅支持Android Lollipop及以上版本</string>
<string name="server_pdomo_primary">PdoMo DNS 主伺服器</string> <string name="server_pdomo_primary">PdoMo DNS 主伺服器</string>
<string name="server_pdomo_secondary">PdoMo DNS 輔伺服器</string> <string name="server_pdomo_secondary">PdoMo DNS 輔伺服器</string>
<string name="server_puredns_south_china">PureDNS 華南</string> <string name="server_puredns_south_china">PureDNS 華南</string>

View File

@ -88,6 +88,12 @@
<string name="settings_server_address">Server Address</string> <string name="settings_server_address">Server Address</string>
<string name="settings_server_port">Server Port</string> <string name="settings_server_port">Server Port</string>
<string name="settings_app_filter">App Filter</string>
<string name="settings_app_filter_mode">Blacklist mode</string>
<string name="settings_app_filter_list">Select application</string>
<string name="settings_app_filter_list_summary">Select an app to filter</string>
<string name="settings_app_filter_summary">Only supports Android Lollipop and above</string>
<string name="server_pdomo_primary">PdoMo DNS Primary</string> <string name="server_pdomo_primary">PdoMo DNS Primary</string>
<string name="server_pdomo_secondary">PdoMo DNS Secondary</string> <string name="server_pdomo_secondary">PdoMo DNS Secondary</string>
<string name="server_puredns_south_china">PureDNS South China</string> <string name="server_puredns_south_china">PureDNS South China</string>

View File

@ -48,6 +48,26 @@
android:title="@string/settings_log_size"/> android:title="@string/settings_log_size"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:key="settings_app_filter"
android:title="@string/settings_app_filter">
<SwitchPreference
android:key="settings_app_filter_switch"
android:title="@string/settings_app_filter"
android:summary="@string/settings_app_filter_summary"
android:defaultValue="false"/>
<SwitchPreference
android:key="settings_app_filter_mode_switch"
android:title="@string/settings_app_filter_mode"
android:defaultValue="false"
android:enabled="false"/>
<org.itxtech.daedalus.widget.ClickPreference
android:key="settings_app_filter_list"
android:title="@string/settings_app_filter_list"
android:summary="@string/settings_app_filter_list_summary"
android:enabled="false"/>
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:key="settings_advanced" android:key="settings_advanced"
android:title="@string/settings_advanced"> android:title="@string/settings_advanced">