Support PreferenceFragmentCompat in New XSharePrefs

This commit is contained in:
2022-04-18 01:11:02 +08:00
parent 9fc69a751b
commit 1541db6427
5 changed files with 227 additions and 5 deletions

View File

@@ -16,6 +16,8 @@
[filename](public/YukiHookModulePrefs.md ':include')
[filename](public/ModulePreferenceFragment.md ':include')
[filename](public/PrefsData.md ':include')
[filename](public/ModuleApplication.md ':include')

View 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.
}
}
```

View File

@@ -26,6 +26,24 @@ class YukiHookModulePrefs(private val context: Context?)
!> 当你在 Xposed 模块中存取数据的时候 `context` 必须不能是空的。
若你正在使用 `PreferenceFragmentCompat`,请迁移到 `ModulePreferenceFragment` 以适配上述功能特性。
### isRunInNewXShareMode [field]
```kotlin
val isRunInNewXShareMode: Boolean
```
**变更记录**
`v1.0.78` `新增`
**功能描述**
> 获取 `YukiHookModulePrefs ` 是否正处于 EdXposed/LSPosed 的最高权限运行。
前提条件为当前 Xposed 模块已被激活。
### name [method]
```kotlin

View File

@@ -31,10 +31,12 @@ package com.highcapable.yukihookapi.hook.xposed.prefs
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceFragmentCompat
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookXposedBridge
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 java.io.File
@@ -53,6 +55,8 @@ import java.io.File
*
* - ❗当你在模块中存取数据的时候 [context] 必须不能是空的
*
* - 若你正在使用 [PreferenceFragmentCompat] - 请迁移到 [ModulePreferenceFragment] 以适配上述功能特性
*
* - 详情请参考 [API 文档 - YukiHookModulePrefs](https://fankes.github.io/YukiHookAPI/#/api/document?id=yukihookmoduleprefs-class)
* @param context 上下文实例 - 默认空
*/
@@ -100,8 +104,8 @@ class YukiHookModulePrefs(private val context: Context? = null) {
/** 是否使用键值缓存 */
private var isUsingKeyValueCache = YukiHookAPI.Configs.isEnableModulePrefsCache
/** 是否新版存储方式 EdXposed/LSPosed */
private var isNewXSharePrefsMode = false
/** 是否使用新版存储方式 EdXposed/LSPosed */
private var isUsingNewXSharePrefs = false
/** 检查 API 装载状态 */
private fun checkApi() {
@@ -128,19 +132,27 @@ class YukiHookModulePrefs(private val context: Context? = null) {
private val sPref
get() = try {
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")
} catch (_: Throwable) {
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")
}
/** 设置全局可读可写 */
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 存储名称
* @param name 自定义的 Sp 存储名称

View File

@@ -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")
}
}