mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-04 17:55:24 +08:00
Support PreferenceFragmentCompat in New XSharePrefs
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
[filename](public/YukiHookModulePrefs.md ':include')
|
[filename](public/YukiHookModulePrefs.md ':include')
|
||||||
|
|
||||||
|
[filename](public/ModulePreferenceFragment.md ':include')
|
||||||
|
|
||||||
[filename](public/PrefsData.md ':include')
|
[filename](public/PrefsData.md ':include')
|
||||||
|
|
||||||
[filename](public/ModuleApplication.md ':include')
|
[filename](public/ModuleApplication.md ':include')
|
||||||
|
81
docs/api/public/ModulePreferenceFragment.md
Normal file
81
docs/api/public/ModulePreferenceFragment.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
## ModulePreferenceFragment [class]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
abstract class ModulePreferenceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.78` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 这是对使用 `YukiHookAPI` Xposed 模块实现中的一个扩展功能。
|
||||||
|
|
||||||
|
此类接管了 `PreferenceFragmentCompat` 并对其实现了 Sp 存储在 Xposed 模块中的全局可读可写。
|
||||||
|
|
||||||
|
在你使用 `PreferenceFragmentCompat` 的实例中,将继承对象换成此类。
|
||||||
|
|
||||||
|
然后请将重写方法由 `onCreatePreferences` 替换为 `onCreatePreferencesInModuleApp` 即可。
|
||||||
|
|
||||||
|
**功能示例**
|
||||||
|
|
||||||
|
使用 `ModulePreferenceFragment` 创建一个 `PreferenceFragmentCompat` 对象。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class SettingsFragment : ModulePreferenceFragment() {
|
||||||
|
|
||||||
|
override fun onCreatePreferencesInModuleApp(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.settings_preferences, rootKey)
|
||||||
|
// Your code here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其余用法与 `PreferenceFragmentCompat` 保持一致。
|
||||||
|
|
||||||
|
### onCreatePreferencesInModuleApp [method]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
abstract fun onCreatePreferencesInModuleApp(savedInstanceState: Bundle?, rootKey: String?)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.78` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 对接原始方法 `onCreatePreferences`。
|
||||||
|
|
||||||
|
### onSharedPreferenceChanged [method]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.78` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 实现了 `SharedPreferences.OnSharedPreferenceChangeListener` 的原生监听功能。
|
||||||
|
|
||||||
|
**功能示例**
|
||||||
|
|
||||||
|
!> 在使用 `onSharedPreferenceChanged` 时请注意保留 super 方法。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class SettingsFragment : ModulePreferenceFragment() {
|
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
|
super.onSharedPreferenceChanged(sharedPreferences, key)
|
||||||
|
// Your code here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@@ -26,6 +26,24 @@ class YukiHookModulePrefs(private val context: Context?)
|
|||||||
|
|
||||||
!> 当你在 Xposed 模块中存取数据的时候 `context` 必须不能是空的。
|
!> 当你在 Xposed 模块中存取数据的时候 `context` 必须不能是空的。
|
||||||
|
|
||||||
|
若你正在使用 `PreferenceFragmentCompat`,请迁移到 `ModulePreferenceFragment` 以适配上述功能特性。
|
||||||
|
|
||||||
|
### isRunInNewXShareMode [field]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val isRunInNewXShareMode: Boolean
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.78` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 获取 `YukiHookModulePrefs ` 是否正处于 EdXposed/LSPosed 的最高权限运行。
|
||||||
|
|
||||||
|
前提条件为当前 Xposed 模块已被激活。
|
||||||
|
|
||||||
### name [method]
|
### name [method]
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
|
@@ -31,10 +31,12 @@ package com.highcapable.yukihookapi.hook.xposed.prefs
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.highcapable.yukihookapi.YukiHookAPI
|
import com.highcapable.yukihookapi.YukiHookAPI
|
||||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookXposedBridge
|
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookXposedBridge
|
||||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.prefs.ui.ModulePreferenceFragment
|
||||||
import de.robv.android.xposed.XSharedPreferences
|
import de.robv.android.xposed.XSharedPreferences
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -53,6 +55,8 @@ import java.io.File
|
|||||||
*
|
*
|
||||||
* - ❗当你在模块中存取数据的时候 [context] 必须不能是空的
|
* - ❗当你在模块中存取数据的时候 [context] 必须不能是空的
|
||||||
*
|
*
|
||||||
|
* - 若你正在使用 [PreferenceFragmentCompat] - 请迁移到 [ModulePreferenceFragment] 以适配上述功能特性
|
||||||
|
*
|
||||||
* - 详情请参考 [API 文档 - YukiHookModulePrefs](https://fankes.github.io/YukiHookAPI/#/api/document?id=yukihookmoduleprefs-class)
|
* - 详情请参考 [API 文档 - YukiHookModulePrefs](https://fankes.github.io/YukiHookAPI/#/api/document?id=yukihookmoduleprefs-class)
|
||||||
* @param context 上下文实例 - 默认空
|
* @param context 上下文实例 - 默认空
|
||||||
*/
|
*/
|
||||||
@@ -100,8 +104,8 @@ class YukiHookModulePrefs(private val context: Context? = null) {
|
|||||||
/** 是否使用键值缓存 */
|
/** 是否使用键值缓存 */
|
||||||
private var isUsingKeyValueCache = YukiHookAPI.Configs.isEnableModulePrefsCache
|
private var isUsingKeyValueCache = YukiHookAPI.Configs.isEnableModulePrefsCache
|
||||||
|
|
||||||
/** 是否为新版存储方式 EdXposed/LSPosed */
|
/** 是否使用新版存储方式 EdXposed/LSPosed */
|
||||||
private var isNewXSharePrefsMode = false
|
private var isUsingNewXSharePrefs = false
|
||||||
|
|
||||||
/** 检查 API 装载状态 */
|
/** 检查 API 装载状态 */
|
||||||
private fun checkApi() {
|
private fun checkApi() {
|
||||||
@@ -128,19 +132,27 @@ class YukiHookModulePrefs(private val context: Context? = null) {
|
|||||||
private val sPref
|
private val sPref
|
||||||
get() = try {
|
get() = try {
|
||||||
checkApi()
|
checkApi()
|
||||||
context?.getSharedPreferences(prefsName, Context.MODE_WORLD_READABLE).also { isNewXSharePrefsMode = true }
|
context?.getSharedPreferences(prefsName, Context.MODE_WORLD_READABLE).also { isUsingNewXSharePrefs = true }
|
||||||
?: error("If you want to use module prefs, you must set the context instance first")
|
?: error("If you want to use module prefs, you must set the context instance first")
|
||||||
} catch (_: Throwable) {
|
} catch (_: Throwable) {
|
||||||
checkApi()
|
checkApi()
|
||||||
context?.getSharedPreferences(prefsName, Context.MODE_PRIVATE).also { isNewXSharePrefsMode = false }
|
context?.getSharedPreferences(prefsName, Context.MODE_PRIVATE).also { isUsingNewXSharePrefs = false }
|
||||||
?: error("If you want to use module prefs, you must set the context instance first")
|
?: error("If you want to use module prefs, you must set the context instance first")
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 设置全局可读可写 */
|
/** 设置全局可读可写 */
|
||||||
private fun makeWorldReadable() = runCatching {
|
private fun makeWorldReadable() = runCatching {
|
||||||
if (isNewXSharePrefsMode.not()) makeWorldReadable(context, prefsFileName = "${prefsName}.xml")
|
if (isUsingNewXSharePrefs.not()) makeWorldReadable(context, prefsFileName = "${prefsName}.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 [YukiHookModulePrefs] 是否正处于 EdXposed/LSPosed 的最高权限运行
|
||||||
|
*
|
||||||
|
* - 前提条件为当前 Xposed 模块已被激活
|
||||||
|
* @return [Boolean] 仅限在模块中判断 - 在宿主 [XSharedPreferences] 环境中始终返回 false
|
||||||
|
*/
|
||||||
|
val isRunInNewXShareMode get() = isUsingNewXSharePrefs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义 Sp 存储名称
|
* 自定义 Sp 存储名称
|
||||||
* @param name 自定义的 Sp 存储名称
|
* @param name 自定义的 Sp 存储名称
|
||||||
|
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/4/17.
|
||||||
|
*/
|
||||||
|
@file:Suppress("WorldReadableFiles", "DEPRECATION")
|
||||||
|
|
||||||
|
package com.highcapable.yukihookapi.hook.xposed.prefs.ui
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import com.highcapable.yukihookapi.YukiHookAPI
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是对使用 [YukiHookAPI] Xposed 模块实现中的一个扩展功能
|
||||||
|
*
|
||||||
|
* 此类接管了 [PreferenceFragmentCompat] 并对其实现了 Sp 存储在 Xposed 模块中的全局可读可写
|
||||||
|
*
|
||||||
|
* 在你使用 [PreferenceFragmentCompat] 的实例中 - 将继承对象换成此类
|
||||||
|
*
|
||||||
|
* 然后请将重写方法由 [onCreatePreferences] 替换为 [onCreatePreferencesInModuleApp] 即可
|
||||||
|
*
|
||||||
|
* 详情请参考 [ModulePreferenceFragment](https://fankes.github.io/YukiHookAPI/#/api/document?id=modulepreferencefragment-class)
|
||||||
|
*/
|
||||||
|
abstract class ModulePreferenceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得 Sp 存储名称
|
||||||
|
* @return [String]
|
||||||
|
*/
|
||||||
|
private val prefsName get() = "${activity?.packageName}_preferences"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 [Fragment] 绑定的 [Activity]
|
||||||
|
* @return [Activity]
|
||||||
|
* @throws IllegalStateException 如果 [Fragment] 已被销毁或未正确装载
|
||||||
|
*/
|
||||||
|
private val currentActivity get() = requireActivity()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取应用默认的 [SharedPreferences]
|
||||||
|
* @return [SharedPreferences]
|
||||||
|
*/
|
||||||
|
private val currentSharedPrefs get() = PreferenceManager.getDefaultSharedPreferences(currentActivity)
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
currentSharedPrefs.registerOnSharedPreferenceChangeListener(this)
|
||||||
|
makeNewXShareReadableIfPossible()
|
||||||
|
onCreatePreferencesInModuleApp(savedInstanceState, rootKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
|
makeNewXShareReadableIfPossible()
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onDestroy() {
|
||||||
|
currentSharedPrefs.unregisterOnSharedPreferenceChangeListener(this)
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对接原始方法 [onCreatePreferences]
|
||||||
|
*
|
||||||
|
* 请重写此方法以实现模块 Sp 存储的自动化设置全局可读可写数据操作
|
||||||
|
* @param savedInstanceState If the fragment is being re-created from a previous saved state, this is the state.
|
||||||
|
* @param rootKey If non-null, this preference fragment should be rooted at the [PreferenceScreen] with this key.
|
||||||
|
*/
|
||||||
|
abstract fun onCreatePreferencesInModuleApp(savedInstanceState: Bundle?, rootKey: String?)
|
||||||
|
|
||||||
|
/** 设置自动适配模块 Sp 存储全局可读可写 */
|
||||||
|
private fun makeNewXShareReadableIfPossible() = try {
|
||||||
|
currentActivity.getSharedPreferences(prefsName, Context.MODE_WORLD_READABLE)
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
YukiHookModulePrefs.makeWorldReadable(currentActivity, prefsFileName = "$prefsName.xml")
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user