Skip to content

Commit

Permalink
ExcludeActivity
Browse files Browse the repository at this point in the history
  • Loading branch information
SaeedDev94 committed Feb 24, 2024
1 parent 9e9b626 commit 99c00e5
Show file tree
Hide file tree
Showing 14 changed files with 331 additions and 17 deletions.
12 changes: 9 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" android:minSdkVersion="34" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission" />

<application
android:allowBackup="true"
Expand Down Expand Up @@ -36,6 +39,9 @@
<activity
android:name=".activity.LogsActivity"
android:parentActivityName=".activity.MainActivity" />
<activity
android:name=".activity.ExcludeActivity"
android:parentActivityName=".activity.MainActivity" />
<activity
android:name=".activity.SettingsActivity"
android:parentActivityName=".activity.MainActivity" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package io.github.saeeddev94.xray.activity

import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.github.saeeddev94.xray.R
import io.github.saeeddev94.xray.Settings
import io.github.saeeddev94.xray.adapter.ExcludeAdapter
import io.github.saeeddev94.xray.databinding.ActivityExcludeBinding
import io.github.saeeddev94.xray.dto.AppList

class ExcludeActivity : AppCompatActivity() {

private lateinit var binding: ActivityExcludeBinding
private lateinit var appsList: RecyclerView
private lateinit var excludeAdapter: ExcludeAdapter
private lateinit var apps: ArrayList<AppList>
private lateinit var filtered: MutableList<AppList>
private lateinit var excludedApps: MutableSet<String>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
title = ""
binding = ActivityExcludeBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)

binding.search.focusable = View.NOT_FOCUSABLE
binding.search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextChange(newText: String?): Boolean {
search(newText)
return false
}

override fun onQueryTextSubmit(query: String?): Boolean {
binding.search.clearFocus()
search(query)
return false
}
})
binding.search.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
?.setOnClickListener {
binding.search.setQuery("", false)
binding.search.clearFocus()
}

getApps()
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_exclude, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.saveExcludedApps -> saveExcludedApps()
else -> finish()
}
return true
}

@SuppressLint("NotifyDataSetChanged")
private fun search(query: String?) {
val keyword = query?.trim()?.lowercase() ?: ""
if (keyword.isEmpty()) {
if (apps.size > filtered.size) {
filtered.clear()
filtered.addAll(apps.toMutableList())
excludeAdapter.notifyDataSetChanged()
}
return
}
val list = ArrayList<AppList>()
apps.forEach {
if (it.appName.lowercase().contains(keyword) || it.packageName.contains(keyword)) {
list.add(it)
}
}
filtered.clear()
filtered.addAll(list.toMutableList())
excludeAdapter.notifyDataSetChanged()
}

private fun getApps() {
Thread {
val selected = ArrayList<AppList>()
val unselected = ArrayList<AppList>()
packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS).forEach {
val permissions = it.requestedPermissions
if (permissions == null || !permissions.contains(Manifest.permission.INTERNET)) return@forEach
val appIcon = it.applicationInfo.loadIcon(packageManager)
val appName = it.applicationInfo.loadLabel(packageManager).toString()
val packageName = it.packageName
val app = AppList(appIcon, appName, packageName)
val isSelected = Settings.excludedApps.contains(packageName)
if (isSelected) selected.add(app) else unselected.add(app)
}
runOnUiThread {
apps = ArrayList(selected + unselected)
filtered = apps.toMutableList()
excludedApps = Settings.excludedApps.split("\n").toMutableSet()
appsList = binding.appsList
excludeAdapter = ExcludeAdapter(this@ExcludeActivity, filtered, excludedApps)
appsList.adapter = excludeAdapter
appsList.layoutManager = LinearLayoutManager(applicationContext)
}
}.start()
}

private fun saveExcludedApps() {
binding.search.clearFocus()
Settings.excludedApps = excludedApps.joinToString("\n")
Settings.save(applicationContext)
finish()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
when (item.itemId) {
R.id.assets -> Intent(applicationContext, AssetsActivity::class.java)
R.id.logs -> Intent(applicationContext, LogsActivity::class.java)
R.id.excludedApps -> Intent(applicationContext, ExcludeActivity::class.java)
R.id.settings -> Intent(applicationContext, SettingsActivity::class.java)
else -> null
}.also {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ class SettingsActivity : AppCompatActivity() {
basic.findViewById<EditText>(R.id.geoSiteAddress).setText(Settings.geoSiteAddress)
basic.findViewById<EditText>(R.id.pingAddress).setText(Settings.pingAddress)
basic.findViewById<EditText>(R.id.pingTimeout).setText(Settings.pingTimeout.toString())
basic.findViewById<EditText>(R.id.excludedApps).setText(Settings.excludedApps)
basic.findViewById<MaterialSwitch>(R.id.bypassLan).isChecked = Settings.bypassLan
basic.findViewById<MaterialSwitch>(R.id.enableIpV6).isChecked = Settings.enableIpV6
basic.findViewById<MaterialSwitch>(R.id.socksUdp).isChecked = Settings.socksUdp
Expand Down Expand Up @@ -91,7 +90,6 @@ class SettingsActivity : AppCompatActivity() {
Settings.geoSiteAddress = basic.findViewById<EditText>(R.id.geoSiteAddress).text.toString()
Settings.pingAddress = basic.findViewById<EditText>(R.id.pingAddress).text.toString()
Settings.pingTimeout = basic.findViewById<EditText>(R.id.pingTimeout).text.toString().toInt()
Settings.excludedApps = basic.findViewById<EditText>(R.id.excludedApps).text.toString()
Settings.bypassLan = basic.findViewById<MaterialSwitch>(R.id.bypassLan).isChecked
Settings.enableIpV6 = basic.findViewById<MaterialSwitch>(R.id.enableIpV6).isChecked
Settings.socksUdp = basic.findViewById<MaterialSwitch>(R.id.socksUdp).isChecked
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.github.saeeddev94.xray.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.checkbox.MaterialCheckBox
import io.github.saeeddev94.xray.R
import io.github.saeeddev94.xray.dto.AppList

class ExcludeAdapter(
private var context: Context,
private var apps: MutableList<AppList>,
private var excludedApps: MutableSet<String>,
) : RecyclerView.Adapter<ExcludeAdapter.ViewHolder>() {

override fun onCreateViewHolder(container: ViewGroup, type: Int): ViewHolder {
val linearLayout = LinearLayout(context)
val item: View = LayoutInflater.from(context).inflate(R.layout.item_recycler_exclude, linearLayout, false)
return ViewHolder(item)
}

override fun getItemCount(): Int {
return apps.size
}

override fun onBindViewHolder(holder: ViewHolder, index: Int) {
val app = apps[index]
val isSelected = excludedApps.contains(app.packageName)
holder.appIcon.setImageDrawable(app.appIcon)
holder.appName.text = app.appName
holder.packageName.text = app.packageName
holder.isSelected.isChecked = isSelected
holder.appContainer.setOnClickListener {
if (isSelected) {
excludedApps.remove(app.packageName)
holder.isSelected.isChecked = false
} else {
excludedApps.add(app.packageName)
holder.isSelected.isChecked = true
}
}
}

class ViewHolder(item: View) : RecyclerView.ViewHolder(item) {
var appContainer: LinearLayout = item.findViewById(R.id.appContainer)
var appIcon: ImageView = item.findViewById(R.id.appIcon)
var appName: TextView = item.findViewById(R.id.appName)
var packageName: TextView = item.findViewById(R.id.packageName)
var isSelected: MaterialCheckBox = item.findViewById(R.id.isSelected)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.github.saeeddev94.xray.component

import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.SearchView

class EmptySubmitSearchView : SearchView {

constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

override fun setOnQueryTextListener(listener: OnQueryTextListener?) {
super.setOnQueryTextListener(listener)
val searchAutoComplete = this.findViewById<SearchAutoComplete>(androidx.appcompat.R.id.search_src_text)
searchAutoComplete.setOnEditorActionListener { _, _, _ ->
listener?.onQueryTextSubmit(query.toString())
return@setOnEditorActionListener true
}
}

}
9 changes: 9 additions & 0 deletions app/src/main/java/io/github/saeeddev94/xray/dto/AppList.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.saeeddev94.xray.dto

import android.graphics.drawable.Drawable

class AppList(
var appIcon: Drawable,
var appName: String,
var packageName: String,
)
11 changes: 11 additions & 0 deletions app/src/main/res/drawable/baseline_alt_route.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M440,880L440,680Q440,624 423,597Q406,570 378,544L435,487Q447,498 458,510.5Q469,523 480,537Q494,518 508.5,503.5Q523,489 538,475Q576,440 607,394Q638,348 640,233L577,296L520,240L680,80L840,240L784,296L720,233Q718,376 676,436.5Q634,497 592,535Q560,564 540,591.5Q520,619 520,680L520,880L440,880ZM248,327Q244,307 242.5,283Q241,259 240,233L176,296L120,240L280,80L440,240L383,296L320,234Q320,255 322,273.5Q324,292 326,308L248,327ZM334,503Q314,482 295.5,454Q277,426 263,385L340,366Q350,393 363,412Q376,431 391,446L334,503Z"/>
</vector>
30 changes: 30 additions & 0 deletions app/src/main/res/layout/activity_exclude.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
tools:context=".activity.ExcludeActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/black">
<io.github.saeeddev94.xray.component.EmptySubmitSearchView
android:id="@+id/search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:iconifiedByDefault="false"
app:queryHint="Search" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/appsList"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
49 changes: 49 additions & 0 deletions app/src/main/res/layout/item_recycler_exclude.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/appContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:baselineAligned="false"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<ImageView
android:id="@+id/appIcon"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="@string/appIcon" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/appName"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/packageName"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/isSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false" />
</LinearLayout>
</LinearLayout>
11 changes: 0 additions & 11 deletions app/src/main/res/layout/tab_basic_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,6 @@
android:autofillHints="" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/excludedApps"
android:labelFor="@id/excludedApps" />
<EditText
android:id="@+id/excludedApps"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:autofillHints="" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/bypassLan"
android:layout_width="match_parent"
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/menu/menu_drawer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
android:id="@+id/logs"
android:icon="@drawable/baseline_adb"
android:title="@string/logs" />
<item
android:id="@+id/excludedApps"
android:icon="@drawable/baseline_alt_route"
android:title="@string/excludedApps" />
<item
android:id="@+id/settings"
android:icon="@drawable/baseline_settings"
Expand Down
Loading

0 comments on commit 99c00e5

Please sign in to comment.