added search to extension list, and bugfix for certain extensions
This commit is contained in:
parent
7aa8b2db52
commit
3007e7d86e
7 changed files with 278 additions and 41 deletions
|
@ -21,7 +21,7 @@ android {
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode ((System.currentTimeMillis() / 60000).toInteger())
|
versionCode ((System.currentTimeMillis() / 60000).toInteger())
|
||||||
versionName "0.0.1"
|
versionName "0.0.2"
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import logcat.LogPriority
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import ani.dantotsu.aniyomi.util.logcat
|
import ani.dantotsu.aniyomi.util.logcat
|
||||||
import ani.dantotsu.aniyomi.util.withUIContext
|
import ani.dantotsu.aniyomi.util.withUIContext
|
||||||
|
import ani.dantotsu.logger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The manager of anime extensions installed as another apk which extend the available sources. It handles
|
* The manager of anime extensions installed as another apk which extend the available sources. It handles
|
||||||
|
@ -116,11 +117,13 @@ class AnimeExtensionManager(
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
println("${sc.name} is not AnimeCatalogueSource")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
logcat(LogPriority.ERROR) { "Error loading anime extension: $result" }
|
logcat(LogPriority.ERROR) { "Error loading anime extension: $result." }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.SearchView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -29,6 +31,7 @@ import ani.dantotsu.databinding.ActivityExtensionsBinding
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
@ -47,16 +50,6 @@ class ExtensionsActivity : AppCompatActivity() {
|
||||||
animeExtensionManager.uninstallExtension(pkgName)
|
animeExtensionManager.uninstallExtension(pkgName)
|
||||||
}
|
}
|
||||||
private val allExtensionsAdapter = AllExtensionsAdapter(lifecycleScope) { pkgName ->
|
private val allExtensionsAdapter = AllExtensionsAdapter(lifecycleScope) { pkgName ->
|
||||||
if (SDK_INT >= VERSION_CODES.O) {
|
|
||||||
// If we don't have permission to install unknown apps, request it
|
|
||||||
if (!packageManager.canRequestPackageInstalls()) {
|
|
||||||
startActivityForResult(
|
|
||||||
Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(
|
|
||||||
Uri.parse("package:$packageName")
|
|
||||||
), 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val notificationManager =
|
val notificationManager =
|
||||||
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
@ -98,7 +91,6 @@ class ExtensionsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -119,12 +111,44 @@ class ExtensionsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
animeExtensionManager.availableExtensionsFlow.collect { extensions ->
|
combine(
|
||||||
allExtensionsAdapter.updateData(extensions)
|
animeExtensionManager.availableExtensionsFlow,
|
||||||
|
animeExtensionManager.installedExtensionsFlow
|
||||||
|
) { availableExtensions, installedExtensions ->
|
||||||
|
// Pair of available and installed extensions
|
||||||
|
Pair(availableExtensions, installedExtensions)
|
||||||
|
}.collect { pair ->
|
||||||
|
val (availableExtensions, installedExtensions) = pair
|
||||||
|
allExtensionsAdapter.updateData(availableExtensions, installedExtensions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val searchView: SearchView = findViewById(R.id.searchView)
|
||||||
|
val extensionsRecyclerView: RecyclerView = findViewById(R.id.extensionsRecyclerView)
|
||||||
|
val extensionsHeader: LinearLayout = findViewById(R.id.extensionsHeader)
|
||||||
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean {
|
||||||
|
if (newText.isNullOrEmpty()) {
|
||||||
|
allExtensionsAdapter.filter("") // Reset the filter
|
||||||
|
allextenstionsRecyclerView.visibility = View.VISIBLE
|
||||||
|
extensionsHeader.visibility = View.VISIBLE
|
||||||
|
extensionsRecyclerView.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
allExtensionsAdapter.filter(newText)
|
||||||
|
allextenstionsRecyclerView.visibility = View.VISIBLE
|
||||||
|
extensionsRecyclerView.visibility = View.GONE
|
||||||
|
extensionsHeader.visibility = View.GONE
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
initActivity(this)
|
initActivity(this)
|
||||||
|
|
||||||
|
|
||||||
|
@ -180,9 +204,10 @@ class ExtensionsActivity : AppCompatActivity() {
|
||||||
private val onButtonClicked: (AnimeExtension.Available) -> Unit) : RecyclerView.Adapter<AllExtensionsAdapter.ViewHolder>() {
|
private val onButtonClicked: (AnimeExtension.Available) -> Unit) : RecyclerView.Adapter<AllExtensionsAdapter.ViewHolder>() {
|
||||||
private var extensions: List<AnimeExtension.Available> = emptyList()
|
private var extensions: List<AnimeExtension.Available> = emptyList()
|
||||||
|
|
||||||
fun updateData(newExtensions: List<AnimeExtension.Available>) {
|
fun updateData(newExtensions: List<AnimeExtension.Available>, installedExtensions: List<AnimeExtension.Installed> = emptyList()) {
|
||||||
extensions = newExtensions
|
val installedPkgNames = installedExtensions.map { it.pkgName }.toSet()
|
||||||
println("Extensions update: $extensions")
|
extensions = newExtensions.filter { it.pkgName !in installedPkgNames }
|
||||||
|
filteredExtensions = extensions
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +218,7 @@ class ExtensionsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val extension = extensions[position]
|
val extension = filteredExtensions[position]
|
||||||
holder.extensionNameTextView.text = extension.name
|
holder.extensionNameTextView.text = extension.name
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val drawable = urlToDrawable(holder.itemView.context, extension.iconUrl)
|
val drawable = urlToDrawable(holder.itemView.context, extension.iconUrl)
|
||||||
|
@ -205,7 +230,18 @@ class ExtensionsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = extensions.size
|
override fun getItemCount(): Int = filteredExtensions.size
|
||||||
|
|
||||||
|
private var filteredExtensions: List<AnimeExtension.Available> = emptyList()
|
||||||
|
|
||||||
|
fun filter(query: String) {
|
||||||
|
filteredExtensions = if (query.isEmpty()) {
|
||||||
|
extensions
|
||||||
|
} else {
|
||||||
|
extensions.filter { it.name.contains(query, ignoreCase = true) }
|
||||||
|
}
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView)
|
val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView)
|
||||||
|
|
33
app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt
Normal file
33
app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package eu.kanade.tachiyomi
|
||||||
|
|
||||||
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by extensions.
|
||||||
|
*/
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
object AppInfo {
|
||||||
|
/**
|
||||||
|
* Version code of the host application. May be useful for sharing as User-Agent information.
|
||||||
|
* Note that this value differs between forks so logic should not rely on it.
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.3
|
||||||
|
*/
|
||||||
|
fun getVersionCode(): Int = ani.dantotsu.BuildConfig.VERSION_CODE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version name of the host application. May be useful for sharing as User-Agent information.
|
||||||
|
* Note that this value differs between forks so logic should not rely on it.
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.3
|
||||||
|
*/
|
||||||
|
fun getVersionName(): String = ani.dantotsu.BuildConfig.VERSION_NAME
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of supported image MIME types by the reader.
|
||||||
|
* e.g. ["image/jpeg", "image/png", ...]
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.5
|
||||||
|
*/
|
||||||
|
fun getSupportedImageMimeTypes(): List<String> = ImageUtil.ImageType.values().map { it.mime }
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package tachiyomi.core.util.system
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.BitmapRegionDecoder
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import android.os.Build
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.core.graphics.alpha
|
||||||
|
import androidx.core.graphics.applyCanvas
|
||||||
|
import androidx.core.graphics.blue
|
||||||
|
import androidx.core.graphics.createBitmap
|
||||||
|
import androidx.core.graphics.get
|
||||||
|
import androidx.core.graphics.green
|
||||||
|
import androidx.core.graphics.red
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
|
import logcat.LogPriority
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.net.URLConnection
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
object ImageUtil {
|
||||||
|
|
||||||
|
|
||||||
|
enum class ImageType(val mime: String, val extension: String) {
|
||||||
|
AVIF("image/avif", "avif"),
|
||||||
|
GIF("image/gif", "gif"),
|
||||||
|
HEIF("image/heif", "heif"),
|
||||||
|
JPEG("image/jpeg", "jpg"),
|
||||||
|
JXL("image/jxl", "jxl"),
|
||||||
|
PNG("image/png", "png"),
|
||||||
|
WEBP("image/webp", "webp"),
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the 'side' part from imageStream and return it as InputStream.
|
||||||
|
*/
|
||||||
|
fun splitInHalf(imageStream: InputStream, side: Side): InputStream {
|
||||||
|
val imageBytes = imageStream.readBytes()
|
||||||
|
|
||||||
|
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||||
|
val height = imageBitmap.height
|
||||||
|
val width = imageBitmap.width
|
||||||
|
|
||||||
|
val singlePage = Rect(0, 0, width / 2, height)
|
||||||
|
|
||||||
|
val half = createBitmap(width / 2, height)
|
||||||
|
val part = when (side) {
|
||||||
|
Side.RIGHT -> Rect(width - width / 2, 0, width, height)
|
||||||
|
Side.LEFT -> Rect(0, 0, width / 2, height)
|
||||||
|
}
|
||||||
|
half.applyCanvas {
|
||||||
|
drawBitmap(imageBitmap, part, singlePage, null)
|
||||||
|
}
|
||||||
|
val output = ByteArrayOutputStream()
|
||||||
|
half.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
||||||
|
|
||||||
|
return ByteArrayInputStream(output.toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rotateImage(imageStream: InputStream, degrees: Float): InputStream {
|
||||||
|
val imageBytes = imageStream.readBytes()
|
||||||
|
|
||||||
|
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||||
|
val rotated = rotateBitMap(imageBitmap, degrees)
|
||||||
|
|
||||||
|
val output = ByteArrayOutputStream()
|
||||||
|
rotated.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
||||||
|
|
||||||
|
return ByteArrayInputStream(output.toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rotateBitMap(bitmap: Bitmap, degrees: Float): Bitmap {
|
||||||
|
val matrix = Matrix().apply { postRotate(degrees) }
|
||||||
|
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split the image into left and right parts, then merge them into a new image.
|
||||||
|
*/
|
||||||
|
fun splitAndMerge(imageStream: InputStream, upperSide: Side): InputStream {
|
||||||
|
val imageBytes = imageStream.readBytes()
|
||||||
|
|
||||||
|
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||||
|
val height = imageBitmap.height
|
||||||
|
val width = imageBitmap.width
|
||||||
|
|
||||||
|
val result = createBitmap(width / 2, height * 2)
|
||||||
|
result.applyCanvas {
|
||||||
|
// right -> upper
|
||||||
|
val rightPart = when (upperSide) {
|
||||||
|
Side.RIGHT -> Rect(width - width / 2, 0, width, height)
|
||||||
|
Side.LEFT -> Rect(0, 0, width / 2, height)
|
||||||
|
}
|
||||||
|
val upperPart = Rect(0, 0, width / 2, height)
|
||||||
|
drawBitmap(imageBitmap, rightPart, upperPart, null)
|
||||||
|
// left -> bottom
|
||||||
|
val leftPart = when (upperSide) {
|
||||||
|
Side.LEFT -> Rect(width - width / 2, 0, width, height)
|
||||||
|
Side.RIGHT -> Rect(0, 0, width / 2, height)
|
||||||
|
}
|
||||||
|
val bottomPart = Rect(0, height, width / 2, height * 2)
|
||||||
|
drawBitmap(imageBitmap, leftPart, bottomPart, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val output = ByteArrayOutputStream()
|
||||||
|
result.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
||||||
|
return ByteArrayInputStream(output.toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Side {
|
||||||
|
RIGHT,
|
||||||
|
LEFT,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val getDisplayMaxHeightInPx: Int
|
||||||
|
get() = Resources.getSystem().displayMetrics.let { max(it.heightPixels, it.widthPixels) }
|
|
@ -17,27 +17,42 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:orientation="horizontal">
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
app:cardBackgroundColor="@color/nav_bg_inv"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="0dp">
|
|
||||||
|
|
||||||
<ImageButton
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/settingsBack"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="64dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="64dp"
|
android:layout_marginTop="32dp"
|
||||||
android:background="@color/nav_bg_inv"
|
app:cardBackgroundColor="@color/nav_bg_inv"
|
||||||
android:padding="16dp"
|
app:cardCornerRadius="16dp"
|
||||||
app:srcCompat="@drawable/ic_round_arrow_back_ios_new_24"
|
app:cardElevation="0dp"
|
||||||
app:tint="@color/bg_opp"
|
>
|
||||||
tools:ignore="ContentDescription,SpeakableTextPresentCheck" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
<ImageButton
|
||||||
|
android:id="@+id/settingsBack"
|
||||||
|
android:layout_width="64dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:background="@color/nav_bg_inv"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:srcCompat="@drawable/ic_round_arrow_back_ios_new_24"
|
||||||
|
app:tint="@color/bg_opp"
|
||||||
|
tools:ignore="ContentDescription,SpeakableTextPresentCheck" />
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<SearchView
|
||||||
|
android:id="@+id/searchView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layoutDirection="rtl"
|
||||||
|
android:layout_gravity="center_vertical"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/extensionsHeader"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
@ -77,20 +92,20 @@
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/extensionsRecyclerView"
|
android:id="@+id/extensionsRecyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"/>
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="32dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textSize="28sp"
|
android:text="All Extensions"
|
||||||
android:text="All Extensions"/>
|
android:textSize="28sp" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/allExtensionsRecyclerView"
|
android:id="@+id/allExtensionsRecyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"/>
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
17
app/src/main/res/xml/network_security_config.xml
Normal file
17
app/src/main/res/xml/network_security_config.xml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Need to allow cleartext traffic for some sources -->
|
||||||
|
<base-config
|
||||||
|
cleartextTrafficPermitted="true"
|
||||||
|
tools:ignore="InsecureBaseConfiguration">
|
||||||
|
<trust-anchors>
|
||||||
|
<!-- Trust preinstalled CAs -->
|
||||||
|
<certificates src="system" />
|
||||||
|
|
||||||
|
<!-- Additionally trust user added CAs -->
|
||||||
|
<certificates
|
||||||
|
src="user"
|
||||||
|
tools:ignore="AcceptsUserCertificates" />
|
||||||
|
</trust-anchors>
|
||||||
|
</base-config>
|
||||||
|
</network-security-config>
|
Loading…
Add table
Add a link
Reference in a new issue