├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── sa90 │ │ └── onepreference │ │ ├── BaseOnePreferenceFragment.kt │ │ ├── OnePreferenceFragmentCompat.kt │ │ ├── SingleColumnPreferenceFragment.kt │ │ ├── TwoColumnPreferenceFragment.kt │ │ ├── adapter │ │ ├── HeaderAdapter.kt │ │ ├── HeaderDiffUtil.kt │ │ └── HeaderViewHolder.kt │ │ ├── helper │ │ ├── OnePreferenceHelper.kt │ │ └── XmlResourceParserExtensions.kt │ │ └── model │ │ └── Header.java │ └── res │ ├── drawable-anydpi │ └── ic_arrow_back.xml │ ├── layout │ └── preference_header_item.xml │ └── values │ ├── attrs.xml │ └── strings.xml ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── sa90 │ │ └── onepreferencedemo │ │ ├── CustomDemoActivity.kt │ │ ├── MainActivity.java │ │ ├── SimpleDemoActivity.java │ │ ├── WebActivity.java │ │ └── fragment │ │ ├── AboutFragment.java │ │ ├── Header1Fragment.java │ │ └── Header2Fragment.java │ └── res │ ├── layout-sw600dp │ ├── content_custom_layout.xml │ └── content_demo_layout.xml │ ├── layout │ ├── activity_custom_demo.xml │ ├── activity_main.xml │ ├── activity_simple_demo_layout.xml │ ├── activity_web.xml │ ├── content_custom_layout.xml │ └── content_demo_layout.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── pref_about.xml │ ├── pref_header1.xml │ ├── pref_header2.xml │ └── pref_headers.xml ├── screenshot.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | bin/ 8 | gen/ 9 | coverage/ 10 | tmp/ 11 | .classpath 12 | .settings/ 13 | .project 14 | lint.xml 15 | project.properties 16 | *.xmi 17 | .metadata 18 | 19 | build.xml 20 | 21 | custom_rules.xml 22 | .idea 23 | */*/build/ 24 | */build 25 | *.iml 26 | app/manifest-merger-production-release-report.txt 27 | infer-out 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OnePreference 2 | ================= 3 | A library which provides the ability to present all your preference in a single list on the phone, without the two-step header and detail approach followed by the Android SDK. For tablets, it uses the two pane layout. See the blog post here: https://medium.com/@saurabharora90/one-pref-for-androids-pride-b254e79f7d88 4 | 5 | ![Tablet - Phone Layout](https://github.com/saurabharora90/OnePreference/blob/develop/screenshot.png?raw=true) 6 | 7 | Change-logs 8 | ------- 9 | Check out the [Release Notes](https://github.com/saurabharora90/OnePreference/releases "Releases") for the change-logs in each release. 10 | 11 | Usage 12 | ------- 13 | Add a dependency to your `build.gradle`: 14 | 15 | dependencies { 16 | compile 'com.sa90.onepreference:library:1.0.2' 17 | } 18 | 19 | The library provides an abstract class (BaseOnePreferenceActivity) and an out of box implementation (OnePreferenceActivity). If you are comfortable with using the out of box implementation, then include it in your manifest file. 20 | 21 | 22 | 23 | Also remember to setup the preference and toolbar style for the OnePreferenceActivity. We are using the material theme for the preference. 24 | 25 | 26 | 31 | 32 | Next you would a headers.xml file (just like the PreferenceActivity) along with their corresponding fragments. 33 | **Note**: The preference fragments should extend from BaseOnePreferenceFragment. 34 | 35 | 36 | 37 | 38 |
40 | 41 |
43 | 44 |
46 | 47 | 48 | 49 | Lastly you can start the OnePreferenceActivity by using the OnePreferenceHelper 50 | 51 | OnePreferenceHelper.startActivity(@XmlRes int headerRes, String title, Activity callingActivity) 52 | 53 | Check out the [Wiki](https://github.com/saurabharora90/OnePreference/wiki) if you need more information about how to customise the out of box toolbar or how to use your own layout for the PreferencesActivity. 54 | License 55 | ------- 56 | 57 | Copyright 2016 Saurabh Arora 58 | 59 | Licensed under the Apache License, Version 2.0 (the "License"); 60 | you may not use this file except in compliance with the License. 61 | You may obtain a copy of the License at 62 | 63 | http://www.apache.org/licenses/LICENSE-2.0 64 | 65 | Unless required by applicable law or agreed to in writing, software 66 | distributed under the License is distributed on an "AS IS" BASIS, 67 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 68 | See the License for the specific language governing permissions and 69 | limitations under the License. 70 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | ext { 6 | // sdk 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | minSdkVersion = 19 10 | 11 | // gradle plugins 12 | androidGradlePluginVersion = '4.1.0-rc01' 13 | 14 | // core dependencies 15 | kotlinVersion = "1.3.72" 16 | 17 | // android dependencies 18 | appCompatVersion = '1.2.0' 19 | coreKtxVersion = '1.3.1' 20 | preference_version = '1.1.1' 21 | fragmentKtxVersion = "1.2.5" 22 | materialVersion = '1.2.0' 23 | recyclerviewVersion = "1.1.0" 24 | 25 | // test dependencies 26 | androidxTestCoreVersion = "1.2.0" 27 | junitVersion = "1.1.1" 28 | mockkVersion = '1.10.0' 29 | robolectricVersion = '4.3.1' 30 | runnerVersion = '1.2.0' 31 | truthVersion = "1.2.0" 32 | } 33 | 34 | repositories { 35 | jcenter() 36 | google() 37 | } 38 | dependencies { 39 | classpath "com.android.tools.build:gradle:$androidGradlePluginVersion" 40 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" 41 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' 42 | classpath "com.github.dcendents:android-maven-gradle-plugin:1.4.1" 43 | 44 | // NOTE: Do not place your application dependencies here; they belong 45 | // in the individual module build.gradle files 46 | } 47 | } 48 | 49 | allprojects { 50 | repositories { 51 | jcenter() 52 | google() 53 | } 54 | } 55 | 56 | task clean(type: Delete) { 57 | delete rootProject.buildDir 58 | } 59 | 60 | subprojects { 61 | tasks.withType(Javadoc).all { enabled = false } 62 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saurabharora90/OnePreference/2c4c0d0c36c27dcd1030da507fe239cfbd0dbe54/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Aug 23 21:21:41 SGT 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion rootProject.compileSdkVersion 6 | 7 | defaultConfig { 8 | minSdkVersion rootProject.minSdkVersion 9 | targetSdkVersion rootProject.targetSdkVersion 10 | versionCode 2 11 | versionName "1.1" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | 25 | kotlinOptions { 26 | jvmTarget = JavaVersion.VERSION_1_8.toString() 27 | } 28 | } 29 | 30 | ext { 31 | // Where you will see your artifact in Bintray's web interface 32 | // The "bintrayName" should match the name of the Bintray repro. 33 | bintrayRepo = 'maven' 34 | bintrayName = 'OnePreference' 35 | 36 | // Maven metadata 37 | publishedGroupId = 'com.sa90.onepreference' 38 | libraryName = 'OnePreference' 39 | // Save yourself a head ache, and set this equal to the name of the Android Studio library 40 | // module. The artifact name needs to match the name of the library. 41 | artifact = 'library' 42 | 43 | libraryDescription = 'An android library to abstract out the preference implementation for Tablets and Phones' 44 | libraryVersion = '1.1' 45 | 46 | developerId = 'saurabharora90' 47 | developerName = 'Saurabh Arora' 48 | developerEmail = 'saurabharora90@gmail.com' 49 | } 50 | 51 | dependencies { 52 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" 53 | 54 | implementation "androidx.appcompat:appcompat:$appCompatVersion" 55 | api "androidx.preference:preference-ktx:$preference_version" 56 | implementation "androidx.fragment:fragment-ktx:$fragmentKtxVersion" 57 | api "androidx.recyclerview:recyclerview:$recyclerviewVersion" 58 | implementation "androidx.core:core-ktx:$coreKtxVersion" 59 | } 60 | 61 | apply from: 'https://raw.githubusercontent.com/attwellBrian/JCenter/master/installv1.gradle' 62 | apply from: 'https://raw.githubusercontent.com/attwellBrian/JCenter/master/bintrayv1.gradle' -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.4.1_1/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/BaseOnePreferenceFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.util.AttributeSet 6 | import android.view.View 7 | import androidx.annotation.XmlRes 8 | import androidx.core.content.withStyledAttributes 9 | import androidx.fragment.app.Fragment 10 | import com.sa90.onepreference.helper.OnePreferenceHelper 11 | import com.sa90.onepreference.model.Header 12 | 13 | abstract class BaseOnePreferenceFragment : Fragment() { 14 | 15 | @XmlRes 16 | private var headerFile: Int = -1 17 | 18 | override fun onInflate(context: Context, attrs: AttributeSet, savedInstanceState: Bundle?) { 19 | super.onInflate(context, attrs, savedInstanceState) 20 | context.withStyledAttributes( 21 | set = attrs, 22 | attrs = R.styleable.BaseOnePreferenceFragment 23 | ) { 24 | headerFile = 25 | getResourceId(R.styleable.BaseOnePreferenceFragment_onepref_headerFile, -1) 26 | } 27 | } 28 | 29 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 30 | if (headerFile != -1) { 31 | val headerList: MutableList
= mutableListOf() 32 | OnePreferenceHelper.loadHeadersFromResource(headerFile, headerList, requireContext()) 33 | addFragments(headerList) 34 | } 35 | } 36 | 37 | internal abstract fun addFragments(headerList: List
) 38 | 39 | fun setHeaders(headersList: List
) { 40 | addFragments(headersList) 41 | } 42 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/OnePreferenceFragmentCompat.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.preference.PreferenceFragmentCompat 6 | 7 | abstract class OnePreferenceFragmentCompat : PreferenceFragmentCompat() { 8 | 9 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 10 | super.onViewCreated(view, savedInstanceState) 11 | listView.overScrollMode = View.OVER_SCROLL_NEVER 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/SingleColumnPreferenceFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.LinearLayout 8 | import androidx.core.view.ViewCompat 9 | import androidx.fragment.app.commit 10 | import com.sa90.onepreference.model.Header 11 | 12 | class SingleColumnPreferenceFragment : BaseOnePreferenceFragment() { 13 | 14 | private val linearLayout by lazy { 15 | LinearLayout(requireContext()) 16 | .apply { 17 | orientation = LinearLayout.VERTICAL 18 | id = ViewCompat.generateViewId() 19 | } 20 | } 21 | 22 | override fun onCreateView( 23 | inflater: LayoutInflater, 24 | container: ViewGroup?, 25 | savedInstanceState: Bundle? 26 | ): View? { 27 | return linearLayout 28 | } 29 | 30 | override fun addFragments(headerList: List
) { 31 | linearLayout.removeAllViews() 32 | 33 | childFragmentManager.commit { 34 | for (header in headerList) { 35 | val fragment = childFragmentManager.fragmentFactory.instantiate( 36 | ClassLoader.getSystemClassLoader(), 37 | header.fragment 38 | ) 39 | fragment.arguments = header.fragmentArguments 40 | add(linearLayout.id, fragment, header.fragment) 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/TwoColumnPreferenceFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.util.AttributeSet 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.widget.LinearLayout 10 | import androidx.annotation.XmlRes 11 | import androidx.core.content.withStyledAttributes 12 | import androidx.core.view.ViewCompat 13 | import androidx.fragment.app.FragmentContainerView 14 | import androidx.fragment.app.commit 15 | import androidx.recyclerview.widget.LinearLayoutManager 16 | import androidx.recyclerview.widget.RecyclerView 17 | import com.sa90.onepreference.adapter.HeaderAdapter 18 | import com.sa90.onepreference.model.Header 19 | 20 | private const val DEFAULT_FIRST_COLUMN_WEIGHT = 0.3f 21 | private const val DEFAULT_SECOND_COLUMN_WEIGHT = 0.3f 22 | 23 | class TwoColumnPreferenceFragment : BaseOnePreferenceFragment() { 24 | 25 | private val recyclerView: RecyclerView by lazy { 26 | RecyclerView(requireContext()) 27 | .apply { 28 | layoutManager = LinearLayoutManager(activity) 29 | } 30 | } 31 | 32 | private val fragmentContainerView: FragmentContainerView by lazy { 33 | FragmentContainerView(requireContext()) 34 | .apply { 35 | id = ViewCompat.generateViewId() 36 | } 37 | } 38 | 39 | private var firstColumnWeight = DEFAULT_FIRST_COLUMN_WEIGHT 40 | private var secondColumnWeight = DEFAULT_SECOND_COLUMN_WEIGHT 41 | 42 | @XmlRes 43 | private var headerFile: Int = -1 44 | 45 | override fun onCreateView( 46 | inflater: LayoutInflater, 47 | container: ViewGroup?, 48 | savedInstanceState: Bundle? 49 | ): View? { 50 | return LinearLayout(requireContext()) 51 | .apply { 52 | orientation = LinearLayout.HORIZONTAL 53 | addView( 54 | recyclerView, 55 | LinearLayout.LayoutParams( 56 | 0, 57 | ViewGroup.LayoutParams.MATCH_PARENT, 58 | firstColumnWeight 59 | ) 60 | ) 61 | addView( 62 | fragmentContainerView, 63 | LinearLayout.LayoutParams( 64 | 0, 65 | ViewGroup.LayoutParams.MATCH_PARENT, 66 | secondColumnWeight 67 | ) 68 | ) 69 | } 70 | } 71 | 72 | override fun onInflate(context: Context, attrs: AttributeSet, savedInstanceState: Bundle?) { 73 | super.onInflate(context, attrs, savedInstanceState) 74 | context.withStyledAttributes( 75 | set = attrs, 76 | attrs = R.styleable.TwoColumnPreferenceFragment 77 | ) { 78 | firstColumnWeight = getFloat( 79 | R.styleable.TwoColumnPreferenceFragment_onepref_firstColumnWeight, 80 | DEFAULT_FIRST_COLUMN_WEIGHT 81 | ) 82 | secondColumnWeight = getFloat( 83 | R.styleable.TwoColumnPreferenceFragment_onepref_secondColumnWeight, 84 | DEFAULT_SECOND_COLUMN_WEIGHT 85 | ) 86 | } 87 | } 88 | 89 | override fun addFragments(headerList: List
) { 90 | recyclerView.adapter = HeaderAdapter { 91 | switchFragment(it) 92 | } 93 | .apply { 94 | submitList(headerList) { 95 | switchFragment(headerList[0]) 96 | } 97 | } 98 | } 99 | 100 | private fun switchFragment(header: Header) { 101 | childFragmentManager.commit { 102 | val fragment = childFragmentManager.fragmentFactory.instantiate( 103 | ClassLoader.getSystemClassLoader(), 104 | header.fragment 105 | ) 106 | fragment.arguments = header.fragmentArguments 107 | replace(fragmentContainerView.id, fragment, header.fragment) 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/adapter/HeaderAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.ListAdapter 6 | import com.sa90.onepreference.R 7 | import com.sa90.onepreference.model.Header 8 | 9 | class HeaderAdapter(private val itemClick: (Header) -> Unit) : ListAdapter( 10 | HeaderDiffUtil() 11 | ) { 12 | override fun onCreateViewHolder( 13 | parent: ViewGroup, 14 | viewType: Int 15 | ): HeaderViewHolder { 16 | val view = LayoutInflater.from(parent.context) 17 | .inflate(R.layout.preference_header_item, parent, false) 18 | return HeaderViewHolder(view, itemClick) 19 | } 20 | 21 | override fun onBindViewHolder( 22 | holder: HeaderViewHolder, 23 | position: Int 24 | ) { 25 | holder.bind(getItem(position)) 26 | } 27 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/adapter/HeaderDiffUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference.adapter 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.sa90.onepreference.model.Header 5 | 6 | class HeaderDiffUtil : DiffUtil.ItemCallback
() { 7 | 8 | override fun areItemsTheSame(oldItem: Header, newItem: Header): Boolean { 9 | return false 10 | } 11 | 12 | override fun areContentsTheSame(oldItem: Header, newItem: Header): Boolean { 13 | return false 14 | } 15 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/adapter/HeaderViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference.adapter 2 | 3 | import android.view.View 4 | import android.widget.ImageView 5 | import android.widget.TextView 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.sa90.onepreference.R 8 | import com.sa90.onepreference.model.Header 9 | 10 | class HeaderViewHolder(itemView: View, itemClick: (Header) -> Unit) : 11 | RecyclerView.ViewHolder(itemView) { 12 | 13 | private val summary: TextView = itemView.findViewById(R.id.tvSummary) 14 | private val title: TextView = itemView.findViewById(R.id.tvTitle) 15 | private val icon: ImageView = itemView.findViewById(R.id.icon) 16 | 17 | init { 18 | itemView.setOnClickListener { itemClick(it.tag as Header) } 19 | } 20 | 21 | fun bind(header: Header) { 22 | itemView.tag = header 23 | if (header.iconRes == 0) { 24 | icon.visibility = View.GONE 25 | } else { 26 | icon.visibility = View.VISIBLE 27 | icon.setImageResource(header.iconRes) 28 | } 29 | title.text = header.getTitle(title.context.resources) 30 | val headerSummary = header.getSummary(title.context.resources) 31 | if (!headerSummary.isNullOrEmpty()) { 32 | summary.visibility = View.VISIBLE 33 | summary.text = headerSummary 34 | } else { 35 | summary.visibility = View.GONE 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/helper/OnePreferenceHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference.helper 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.content.res.XmlResourceParser 6 | import android.os.Bundle 7 | import android.util.TypedValue 8 | import android.util.Xml 9 | import androidx.annotation.XmlRes 10 | import androidx.core.content.withStyledAttributes 11 | import com.sa90.onepreference.R 12 | import com.sa90.onepreference.model.Header 13 | import org.xmlpull.v1.XmlPullParser 14 | import org.xmlpull.v1.XmlPullParserException 15 | import java.io.IOException 16 | 17 | /** 18 | * The helper parses the header file to create a list of [Header] objects 19 | */ 20 | object OnePreferenceHelper { 21 | private const val HEADER_ID_UNDEFINED: Long = -1 22 | 23 | /** 24 | * Parse the given XML file as a header description, adding each 25 | * parsed Header into the target list. Adopted from AOSP 26 | * 27 | * @param resid The XML resource to load and parse. 28 | * @param target The list in which the parsed headers should be placed. 29 | */ 30 | @JvmStatic 31 | fun loadHeadersFromResource( 32 | @XmlRes resid: Int, 33 | target: MutableList
, 34 | context: Context 35 | ) { 36 | var parser: XmlResourceParser? = null 37 | try { 38 | parser = context.resources.getXml(resid) 39 | val attrs = Xml.asAttributeSet(parser) 40 | var type: Int 41 | while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT 42 | && type != XmlPullParser.START_TAG 43 | ) { 44 | // Parse next until start tag is found 45 | } 46 | var nodeName = parser.name 47 | if ("preference-headers" != nodeName) { 48 | throw RuntimeException( 49 | "XML document must start with tag; found" 50 | + nodeName + " at " + parser.positionDescription 51 | ) 52 | } 53 | 54 | val outerDepth = parser.depth 55 | while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT 56 | && (type != XmlPullParser.END_TAG || parser.depth > outerDepth) 57 | ) { 58 | if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 59 | continue 60 | } 61 | nodeName = parser.name 62 | if ("header" == nodeName) { 63 | context.withStyledAttributes(attrs, R.styleable.PreferenceHeader) { 64 | val header = Header() 65 | var curBundle: Bundle? = null 66 | 67 | header.id = getResourceId( 68 | R.styleable.PreferenceHeader_id, 69 | HEADER_ID_UNDEFINED.toInt() 70 | ).toLong() 71 | var tv = peekValue( 72 | R.styleable.PreferenceHeader_title 73 | ) 74 | if (tv != null && tv.type == TypedValue.TYPE_STRING) { 75 | if (tv.resourceId != 0) { 76 | header.titleRes = tv.resourceId 77 | } else { 78 | header.title = tv.string 79 | } 80 | } 81 | tv = peekValue( 82 | R.styleable.PreferenceHeader_summary 83 | ) 84 | if (tv != null && tv.type == TypedValue.TYPE_STRING) { 85 | if (tv.resourceId != 0) { 86 | header.summaryRes = tv.resourceId 87 | } else { 88 | header.summary = tv.string 89 | } 90 | } 91 | header.iconRes = getResourceId( 92 | R.styleable.PreferenceHeader_icon, 0 93 | ) 94 | header.fragment = getString( 95 | R.styleable.PreferenceHeader_fragment 96 | ) 97 | if (curBundle == null) { 98 | curBundle = Bundle() 99 | } 100 | val innerDepth = parser.depth 101 | while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT 102 | && (type != XmlPullParser.END_TAG || parser.depth > innerDepth) 103 | ) { 104 | if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 105 | continue 106 | } 107 | val innerNodeName = parser.name 108 | if (innerNodeName == "extra") { 109 | context.resources.parseBundleExtra("extra", attrs, curBundle) 110 | parser.skipCurrentTag() 111 | } else if (innerNodeName == "intent") { 112 | header.intent = 113 | Intent.parseIntent(context.resources, parser, attrs) 114 | } else { 115 | parser.skipCurrentTag() 116 | } 117 | } 118 | if (curBundle.size() > 0) { 119 | header.fragmentArguments = curBundle 120 | curBundle = null 121 | } 122 | target.add(header) 123 | } 124 | } else { 125 | parser.skipCurrentTag() 126 | } 127 | } 128 | } catch (e: XmlPullParserException) { 129 | throw RuntimeException("Error parsing headers", e) 130 | } catch (e: IOException) { 131 | throw RuntimeException("Error parsing headers", e) 132 | } finally { 133 | parser?.close() 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/helper/XmlResourceParserExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference.helper 2 | 3 | import org.xmlpull.v1.XmlPullParser 4 | 5 | internal fun XmlPullParser.skipCurrentTag(): Unit { 6 | val outerDepth: Int = depth 7 | var type: Int 8 | while (next().also { type = it } != XmlPullParser.END_DOCUMENT 9 | && (type != XmlPullParser.END_TAG 10 | || depth > outerDepth) 11 | ) { 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/java/com/sa90/onepreference/model/Header.java: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreference.model; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Resources; 5 | import android.os.Bundle; 6 | import android.os.Parcel; 7 | import android.os.Parcelable; 8 | import android.preference.PreferenceActivity; 9 | import android.text.TextUtils; 10 | 11 | import androidx.annotation.StringRes; 12 | 13 | /** 14 | * Adopted from AOSP 15 | * Description of a single Header item that the user can select. 16 | */ 17 | public final class Header implements Parcelable { 18 | 19 | /** 20 | * Default value for {@link PreferenceActivity.Header#id Header.id} indicating that no 21 | * identifier value is set. All other values (including those below -1) 22 | * are valid. 23 | */ 24 | public static final long HEADER_ID_UNDEFINED = -1; 25 | 26 | /** 27 | * Identifier for this header, to correlate with a new list when 28 | * it is updated. The default value is 29 | * {@link PreferenceActivity#HEADER_ID_UNDEFINED}, meaning no id. 30 | * @attr ref android.R.styleable#PreferenceHeader_id 31 | */ 32 | public long id = HEADER_ID_UNDEFINED; 33 | 34 | /** 35 | * Resource ID of title of the header that is shown to the user. 36 | * @attr ref android.R.styleable#PreferenceHeader_title 37 | */ 38 | @StringRes 39 | public int titleRes; 40 | 41 | /** 42 | * Title of the header that is shown to the user. 43 | * @attr ref android.R.styleable#PreferenceHeader_title 44 | */ 45 | public CharSequence title; 46 | 47 | /** 48 | * Resource ID of optional summary describing what this header controls. 49 | * @attr ref android.R.styleable#PreferenceHeader_summary 50 | */ 51 | @StringRes 52 | public int summaryRes; 53 | 54 | /** 55 | * Optional summary describing what this header controls. 56 | * @attr ref android.R.styleable#PreferenceHeader_summary 57 | */ 58 | public CharSequence summary; 59 | 60 | /** 61 | * Optional icon resource to show for this header. 62 | * @attr ref android.R.styleable#PreferenceHeader_icon 63 | */ 64 | public int iconRes; 65 | 66 | /** 67 | * Full class name of the fragment to display when this header is 68 | * selected. 69 | * @attr ref android.R.styleable#PreferenceHeader_fragment 70 | */ 71 | public String fragment; 72 | 73 | /** 74 | * Optional arguments to supply to the fragment when it is 75 | * instantiated. 76 | */ 77 | public Bundle fragmentArguments; 78 | 79 | /** 80 | * Intent to launch when the preference is selected. 81 | */ 82 | public Intent intent; 83 | 84 | public Header() { 85 | // Empty 86 | } 87 | 88 | /** 89 | * Return the currently set title. If {@link #titleRes} is set, 90 | * this resource is loaded from res and returned. Otherwise 91 | * {@link #title} is returned. 92 | */ 93 | public CharSequence getTitle(Resources res) { 94 | if (titleRes != 0) { 95 | return res.getText(titleRes); 96 | } 97 | return title; 98 | } 99 | 100 | /** 101 | * Return the currently set summary. If {@link #summaryRes} is set, 102 | * this resource is loaded from res and returned. Otherwise 103 | * {@link #summary} is returned. 104 | */ 105 | public CharSequence getSummary(Resources res) { 106 | if (summaryRes != 0) { 107 | return res.getText(summaryRes); 108 | } 109 | return summary; 110 | } 111 | 112 | @Override 113 | public int describeContents() { 114 | return 0; 115 | } 116 | 117 | @Override 118 | public void writeToParcel(Parcel dest, int flags) { 119 | dest.writeLong(id); 120 | dest.writeInt(titleRes); 121 | TextUtils.writeToParcel(title, dest, flags); 122 | dest.writeInt(summaryRes); 123 | TextUtils.writeToParcel(summary, dest, flags); 124 | dest.writeInt(iconRes); 125 | dest.writeString(fragment); 126 | dest.writeBundle(fragmentArguments); 127 | if (intent != null) { 128 | dest.writeInt(1); 129 | intent.writeToParcel(dest, flags); 130 | } else { 131 | dest.writeInt(0); 132 | } 133 | } 134 | 135 | public void readFromParcel(Parcel in) { 136 | id = in.readLong(); 137 | titleRes = in.readInt(); 138 | title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 139 | summaryRes = in.readInt(); 140 | summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 141 | iconRes = in.readInt(); 142 | fragment = in.readString(); 143 | fragmentArguments = in.readBundle(getClass().getClassLoader()); 144 | if (in.readInt() != 0) { 145 | intent = Intent.CREATOR.createFromParcel(in); 146 | } 147 | } 148 | 149 | Header(Parcel in) { 150 | readFromParcel(in); 151 | } 152 | 153 | public static final Creator
CREATOR = new Creator
() { 154 | public Header createFromParcel(Parcel source) { 155 | return new Header(source); 156 | } 157 | public Header[] newArray(int size) { 158 | return new Header[size]; 159 | } 160 | }; 161 | } 162 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-anydpi/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /library/src/main/res/layout/preference_header_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 24 | 25 | 33 | 34 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | One Preference 3 | 4 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion rootProject.compileSdkVersion 6 | defaultConfig { 7 | multiDexEnabled = true 8 | applicationId "com.sa90.onepreferencedemo" 9 | minSdkVersion rootProject.minSdkVersion 10 | targetSdkVersion rootProject.targetSdkVersion 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation project(':library') 29 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" 30 | implementation "androidx.appcompat:appcompat:$appCompatVersion" 31 | implementation "com.google.android.material:material:$materialVersion" 32 | //implementation 'com.sa90.onepreference:library:2.0.0' 33 | } 34 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.4.1_1/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /sample/src/main/java/com/sa90/onepreferencedemo/CustomDemoActivity.kt: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreferencedemo 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.appcompat.widget.Toolbar 7 | import com.google.android.material.floatingactionbutton.FloatingActionButton 8 | import com.sa90.onepreference.BaseOnePreferenceFragment 9 | import com.sa90.onepreference.helper.OnePreferenceHelper 10 | import com.sa90.onepreference.model.Header 11 | 12 | class CustomDemoActivity : AppCompatActivity() { 13 | 14 | private lateinit var onePreferenceFragment: BaseOnePreferenceFragment 15 | 16 | private val headerList: MutableList
= mutableListOf() 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | setContentView(R.layout.activity_custom_demo) 21 | 22 | val toolbar = 23 | findViewById(R.id.toolbar) 24 | setSupportActionBar(toolbar) 25 | 26 | supportActionBar!!.setDisplayHomeAsUpEnabled(true) 27 | 28 | val fab = findViewById(R.id.fab) 29 | fab.setOnClickListener { 30 | if (headerList.size > 0) { 31 | headerList.removeAt(0) 32 | onePreferenceFragment.setHeaders(headerList) 33 | } 34 | 35 | it.visibility = View.GONE 36 | } 37 | 38 | onePreferenceFragment = 39 | supportFragmentManager.findFragmentByTag("my_tag") as BaseOnePreferenceFragment 40 | 41 | OnePreferenceHelper.loadHeadersFromResource(R.xml.pref_headers, headerList, this) 42 | onePreferenceFragment.setHeaders(headerList) 43 | } 44 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/sa90/onepreferencedemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreferencedemo; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import androidx.appcompat.app.AppCompatActivity; 8 | import androidx.appcompat.widget.Toolbar; 9 | 10 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_main); 16 | 17 | findViewById(R.id.btnNormal).setOnClickListener(this); 18 | findViewById(R.id.btnCustom).setOnClickListener(this); 19 | 20 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 21 | setSupportActionBar(toolbar); 22 | } 23 | 24 | @Override 25 | public void onClick(View v) { 26 | switch (v.getId()) { 27 | case R.id.btnNormal: 28 | startActivity(new Intent(MainActivity.this, SimpleDemoActivity.class)); 29 | break; 30 | case R.id.btnCustom: 31 | startActivity(new Intent(MainActivity.this, CustomDemoActivity.class)); 32 | break; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sample/src/main/java/com/sa90/onepreferencedemo/SimpleDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreferencedemo; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.appcompat.app.AppCompatActivity; 6 | import androidx.appcompat.widget.Toolbar; 7 | 8 | public class SimpleDemoActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_simple_demo_layout); 14 | Toolbar toolbar = findViewById(R.id.toolbar); 15 | setSupportActionBar(toolbar); 16 | 17 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sample/src/main/java/com/sa90/onepreferencedemo/WebActivity.java: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreferencedemo; 2 | 3 | import android.os.Bundle; 4 | import android.webkit.WebView; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | public class WebActivity extends AppCompatActivity { 9 | 10 | WebView wv; 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_web); 16 | 17 | wv = (WebView) findViewById(R.id.wv); 18 | 19 | wv.loadUrl(getIntent().getStringExtra("extra_url")); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sample/src/main/java/com/sa90/onepreferencedemo/fragment/AboutFragment.java: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreferencedemo.fragment; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.sa90.onepreference.OnePreferenceFragmentCompat; 6 | import com.sa90.onepreferencedemo.R; 7 | 8 | public class AboutFragment extends OnePreferenceFragmentCompat { 9 | 10 | @Override 11 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 12 | setPreferencesFromResource(R.xml.pref_about, rootKey); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sample/src/main/java/com/sa90/onepreferencedemo/fragment/Header1Fragment.java: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreferencedemo.fragment; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.sa90.onepreference.OnePreferenceFragmentCompat; 6 | import com.sa90.onepreferencedemo.R; 7 | 8 | public class Header1Fragment extends OnePreferenceFragmentCompat { 9 | 10 | @Override 11 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 12 | setPreferencesFromResource(R.xml.pref_header1, rootKey); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sample/src/main/java/com/sa90/onepreferencedemo/fragment/Header2Fragment.java: -------------------------------------------------------------------------------- 1 | package com.sa90.onepreferencedemo.fragment; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.sa90.onepreference.OnePreferenceFragmentCompat; 6 | import com.sa90.onepreferencedemo.R; 7 | 8 | public class Header2Fragment extends OnePreferenceFragmentCompat { 9 | 10 | @Override 11 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 12 | setPreferencesFromResource(R.xml.pref_header2, rootKey); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sample/src/main/res/layout-sw600dp/content_custom_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sample/src/main/res/layout-sw600dp/content_demo_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_custom_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 25 | 26 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_simple_demo_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_web.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/content_custom_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/content_demo_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saurabharora90/OnePreference/2c4c0d0c36c27dcd1030da507fe239cfbd0dbe54/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saurabharora90/OnePreference/2c4c0d0c36c27dcd1030da507fe239cfbd0dbe54/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saurabharora90/OnePreference/2c4c0d0c36c27dcd1030da507fe239cfbd0dbe54/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saurabharora90/OnePreference/2c4c0d0c36c27dcd1030da507fe239cfbd0dbe54/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saurabharora90/OnePreference/2c4c0d0c36c27dcd1030da507fe239cfbd0dbe54/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | One Preference Demo 3 | 4 | Header 1 5 | Header 2 6 | About 7 | Removable 8 | PhonePreferenceActivity 9 | CustomLayoutActivity 10 | 11 | key_remove 12 | 13 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 |