____ _ _______ __
/ __ \ | /| / / __/ |/ /
/ /_/ / |/ |/ / _// /
\____/|__/|__/___/_||_/
owenclave / app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
/******************************************************************************
* *
* Copyright (C) 2021 by nekohasekai *
* *
* 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. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
******************************************************************************/
package io.nekohasekai.sagernet.ui
import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.os.Build
import android.os.Bundle
import android.os.PowerManager
import android.provider.Settings
import android.text.util.Linkify
import android.view.View
import android.widget.ArrayAdapter
import android.widget.HorizontalScrollView
import android.widget.ListView
import android.widget.TextView
import androidx.activity.result.component1
import androidx.activity.result.component2
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.setPadding
import androidx.core.view.updatePadding
import androidx.core.widget.NestedScrollView
import androidx.recyclerview.widget.RecyclerView
import com.danielstone.materialaboutlibrary.MaterialAboutFragment
import com.danielstone.materialaboutlibrary.items.MaterialAboutActionItem
import com.danielstone.materialaboutlibrary.items.MaterialAboutTitleItem
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
import com.danielstone.materialaboutlibrary.model.MaterialAboutList
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.nekohasekai.sagernet.BuildConfig
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.ktx.dp2px
import io.nekohasekai.sagernet.ktx.dp2pxf
import io.nekohasekai.sagernet.ktx.snackbar
import libexclavecore.Libexclavecore
class AboutFragment : ToolbarFragment(R.layout.layout_about) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setTitle(R.string.menu_about)
ViewCompat.setOnApplyWindowInsetsListener(view.findViewById(R.id.layout_about)) { v, insets ->
val bars = insets.getInsets(
WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout()
)
v.updatePadding(
left = bars.left,
right = bars.right,
bottom = bars.bottom + dp2px(64),
)
insets
}
parentFragmentManager.beginTransaction()
.replace(R.id.about_fragment_holder, AboutContent())
.commitAllowingStateLoss()
(requireActivity() as? MainActivity)?.onBackPressedCallback?.isEnabled = true
}
class AboutContent : MaterialAboutFragment() {
val requestIgnoreBatteryOptimizations = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { (resultCode, _) ->
parentFragmentManager.beginTransaction()
.replace(R.id.about_fragment_holder, AboutContent())
.commitAllowingStateLoss()
}
override fun getMaterialAboutList(activityContext: Context?): MaterialAboutList {
try {
if (activityContext == null) {
return MaterialAboutList.Builder().build()
}
return MaterialAboutList.Builder()
.addCard(MaterialAboutCard.Builder()
.outline(false)
.addItem(MaterialAboutTitleItem.Builder()
.icon(R.mipmap.ic_launcher)
.text(R.string.app_name)
.setOnLongClickAction {
MaterialAlertDialogBuilder(activityContext).apply {
setView(NestedScrollView(activityContext).apply {
setPadding(dp2px(16), dp2px(16), dp2px(16), 0)
addView( HorizontalScrollView(activityContext).apply {
addView(TextView(activityContext).apply {
text = resources.openRawResource(R.raw.aboutlibraries).bufferedReader().use { it.readText() }
textSize = dp2pxf(4)
typeface = Typeface.MONOSPACE
isSingleLine = false
setTextIsSelectable(true)
setHorizontallyScrolling(false)
})
})
})
setPositiveButton(android.R.string.ok, null)
setNegativeButton(android.R.string.cancel, null)
}.show()
}
.build())
.addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_update_24)
.text(R.string.app_version)
.subText(BuildConfig.VERSION_NAME)
.setOnClickAction {
startActivity(Intent(
Intent.ACTION_VIEW,
"https://github.com/owenewans/owenclave/releases".toUri()
))
}
.setOnLongClickAction {
DataStore.enableDebug = !DataStore.enableDebug
snackbar(if (DataStore.enableDebug) "developer mode enabled" else "developer mode disabled").show()
}
.build())
.addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_airplanemode_active_24)
.text(getString(R.string.version_x, "owenclave-core"))
.subText("xray=${Libexclavecore.getV2RayVersion()}")
.setOnClickAction {
startActivity(Intent(
Intent.ACTION_VIEW,
"https://github.com/owenewans/owenclave-core".toUri()
))
}
.build())
.addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_info_24)
.text(getString(R.string.version_x, "Go"))
.subText(Libexclavecore.getGoVersion())
.setOnLongClickAction {
MaterialAlertDialogBuilder(activityContext).apply {
setView(NestedScrollView(activityContext).apply {
setPadding(dp2px(16), dp2px(16), dp2px(16), 0)
addView( HorizontalScrollView(activityContext).apply {
addView(TextView(activityContext).apply {
text = Libexclavecore.getDepInfo()
textSize = dp2pxf(4)
typeface = Typeface.MONOSPACE
isSingleLine = false
setTextIsSelectable(true)
setHorizontallyScrolling(false)
})
})
})
setPositiveButton(android.R.string.ok, null)
setNegativeButton(android.R.string.cancel, null)
}.show()
}
.build())
.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = activityContext.getSystemService(Context.POWER_SERVICE) as PowerManager
if (!pm.isIgnoringBatteryOptimizations(activityContext.packageName)) {
addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_running_with_errors_24)
.text(R.string.ignore_battery_optimizations)
.subText(R.string.ignore_battery_optimizations_sum)
.setOnClickAction {
requestIgnoreBatteryOptimizations.launch(
Intent(
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
"package:${activityContext.packageName}".toUri()
)
)
}.build())
}
}
}
.build())
.addCard(MaterialAboutCard.Builder()
.outline(false)
.title(R.string.project)
.addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_sanitizer_24)
.text(R.string.github)
.setOnClickAction {
startActivity(Intent(
Intent.ACTION_VIEW,
"https://github.com/owenewans/owenclave".toUri()
))
}
.build())
.addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.baseline_translate_24)
.text(R.string.translation_platform)
.setOnClickAction {
startActivity(Intent(
Intent.ACTION_VIEW,
"https://hosted.weblate.org/projects/owenclave/".toUri()
))
}
.build())
.addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_action_copyleft)
.text(R.string.license)
.setOnClickAction {
MaterialAlertDialogBuilder(activityContext).apply {
setView(
TextView(activityContext).apply {
setPadding(dp2px(16))
text = getString(
if (Libexclavecore.buildWithClash()) {
R.string.license_gpl_v3_only
} else {
R.string.license_gpl_v3_or_later
}
)
setTextIsSelectable(true)
Linkify.addLinks(this, Linkify.EMAIL_ADDRESSES or Linkify.WEB_URLS)
}
)
setOnCancelListener { _ ->
showLicenseAlertDialogFromAssets(activityContext, "license/GPL-3.0.txt")
}
setPositiveButton(android.R.string.ok) { _, _ ->
showLicenseAlertDialogFromAssets(activityContext, "license/GPL-3.0.txt")
}
setNegativeButton(android.R.string.cancel, null)
}.show()
}
.build())
.addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_action_description)
.text(R.string.third_party_notices)
.setOnClickAction {
MaterialAlertDialogBuilder(activityContext).apply {
setView(ListView(activityContext).apply {
adapter = ArrayAdapter(activityContext, android.R.layout.simple_list_item_1,
arrayOf(
getString(R.string.third_party_notices),
"Apache License Version 2.0",
"MIT License",
"BSD 3-Clause License",
"BSD 2-Clause License",
"GNU GENERAL PUBLIC LICENSE Version 3",
"Mozilla Public License Version 2.0",
"Creative Commons Attribution 4.0 International Public License",
"Community Data License Agreement - Permissive - Version 2.0"
)
)
setOnItemClickListener { _, _, position, _ ->
showLicenseAlertDialogFromAssets(activityContext, when (position) {
0 -> "license/notices.txt"
1 -> "license/Apache-2.0.txt"
2 -> "license/MIT.txt"
3 -> "license/BSD-3-Clause.txt"
4 -> "license/BSD-2-Clause.txt"
5 -> "license/GPL-3.0.txt"
6 -> "license/MPL-2.0.txt"
7 -> "license/CC-BY-4.0.txt"
8 -> "license/CDLA-Permissive-2.0.txt"
else -> error("index out of bound")
})
}
})
setPositiveButton(android.R.string.ok, null)
setNegativeButton(android.R.string.cancel, null)
}.show()
}
.build())
.build())
.build()
} catch (_: IllegalStateException) {
return MaterialAboutList.Builder().build()
}
}
private fun showLicenseAlertDialogFromAssets(context: Context, asset: String) {
MaterialAlertDialogBuilder(context).apply {
setView(NestedScrollView(context).apply {
setPadding(dp2px(16), dp2px(16), dp2px(16), 0)
addView( HorizontalScrollView(context).apply {
addView(TextView(context).apply {
text = context.assets.open(asset).use { it.reader().readText() }
textSize = dp2pxf(4)
typeface = Typeface.MONOSPACE
isSingleLine = false
setTextIsSelectable(true)
setHorizontallyScrolling(false)
})
})
})
setPositiveButton(android.R.string.ok, null)
setNegativeButton(android.R.string.cancel, null)
}.show()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById(com.danielstone.materialaboutlibrary.R.id.mal_recyclerview).apply {
overScrollMode = RecyclerView.OVER_SCROLL_ALWAYS
}
}
}
}