├── .gitignore ├── LICENSE.txt ├── README.md ├── app-no-rx ├── .gitignore ├── build.gradle └── src │ ├── androidTest │ └── java │ │ └── proxypref │ │ └── test │ │ └── TestActivityTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── proxypref │ │ └── test │ │ └── TestActivity.java │ └── res │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ └── values │ └── strings.xml ├── app ├── .gitignore ├── build.gradle └── src │ ├── androidTest │ └── java │ │ └── proxypref │ │ └── test │ │ └── TestActivityTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── proxypref │ │ └── test │ │ └── TestActivity.java │ └── res │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ └── values │ └── strings.xml ├── build.gradle ├── gradle.properties ├── gradle ├── gradle-mvn-push.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── proxypref ├── .gitignore ├── build.gradle ├── gradle.properties └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── proxypref │ │ ├── ProxyHandler.java │ │ ├── ProxyPreferences.java │ │ ├── annotation │ │ ├── DefaultBoolean.java │ │ ├── DefaultFloat.java │ │ ├── DefaultInteger.java │ │ ├── DefaultLong.java │ │ ├── DefaultSet.java │ │ ├── DefaultString.java │ │ └── Preference.java │ │ └── method │ │ ├── DataType.java │ │ ├── MethodInfo.java │ │ ├── MethodType.java │ │ ├── OnSharedPreferenceChangeListenerOnSubscribe.java │ │ └── RxDelegate.java │ └── test │ └── java │ └── proxypref │ ├── Coverage.java │ ├── DefaultValuesTest.java │ ├── ProxyHandlerAssertionsTest.java │ ├── ProxyHandlerKeyTest.java │ ├── ProxyHandlerRxAssertionsTest.java │ ├── ProxyHandlerRxTest.java │ ├── ProxyHandlerTest.java │ ├── ProxyHandlerTypesTest.java │ ├── TestUtil.java │ └── method │ └── Coverage.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /local.properties 3 | /.idea 4 | /build 5 | /captures 6 | *.iml 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Konstantin Mikheev sirstripy-at-gmail-com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProxyPref 2 | 3 | ProxyPref is a simple Android library, which allows to easily access SharedPreferences. 4 | 5 | ### Definition 6 | 7 | Define an interface with set/get method for types: 8 | `String`, `Integer`, `Long`, `Float`, `Boolean`, `Set` 9 | 10 | Keys are method names from the interface, but without `set`/`get` prefixes. 11 | You can omit `set`/`get` part if you like. 12 | 13 | Annotate fields with `Preference` annotation to set a preference key name if you're going to use ProGuard. 14 | 15 | ##### RxJava 16 | 17 | There is also `rx.Observable` and `rx.functions.Action1` interface support to chain 18 | preferences into RxJava operators. 19 | 20 | ##### null 21 | 22 | `null` is *does not exist* value. If you don't want to get nulls - define default values with 23 | `DefaultString`, `DefaultInteger`, `DefaultLong`, `DefaultFloat`, `DefaultBoolean`, `DefaultSet`. 24 | 25 | You can also remove a key-value pair from preferences by passing `null` into the set method. 26 | 27 | ``` java 28 | interface MyPreferences { 29 | 30 | // access with get/set prefix 31 | String getTestString(); // key = testString 32 | void setTestString(String x); // key = testString 33 | 34 | // without get/set prefix 35 | Integer testInteger(); // key = testInteger 36 | void testInteger(Integer x); // key = testInteger 37 | 38 | // observe with rx.Observable 39 | Observable lastSelectedItem(); // key = lastSelectedItem 40 | 41 | // set with rx.functions.Action1 42 | Action1 setLastSelectedItem(); // key = lastSelectedItem 43 | 44 | // ProGuard ready 45 | @Preference("username") 46 | String a12(); // key = username 47 | 48 | // Default value 49 | @DefaultString("user256") 50 | String username(); // key = username 51 | 52 | // Default set 53 | @DefaultSet({"1", "2", "3"}) 54 | Set getSomeSet(); // key = someSet 55 | } 56 | ``` 57 | 58 | ### Usage 59 | 60 | Is easy! 61 | 62 | ``` java 63 | MyPreferences pref = ProxyPreferences 64 | .build(MyPreferences.class, getSharedPreferences("preferences", 0)); 65 | 66 | Log.v("test", pref.username()); 67 | ``` 68 | 69 | ##### OR 70 | 71 | ``` java 72 | MyPreferences pref = ProxyPreferences 73 | .buildWithRx(MyPreferences.class, getSharedPreferences("preferences", 0)); 74 | ``` 75 | 76 | If you want to have RxJava features. Don't forget to include RxJava itself. ;) 77 | 78 | ##### RxJava warning 79 | 80 | Keep hard references to RxJava subscriptions otherwise they can be GC'ed due to the `SharedPreferences` bug. 81 | 82 | ### Dependency 83 | 84 | ``` guava 85 | compile 'info.android15.proxypref:proxypref:0.2.0' 86 | ``` 87 | 88 | -------------------------------------------------------------------------------- /app-no-rx/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app-no-rx/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "proxypref.test" 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | 15 | defaultConfig { 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | } 18 | } 19 | 20 | dependencies { 21 | compile project(':proxypref') 22 | androidTestCompile 'com.android.support.test:runner:0.4' 23 | androidTestCompile 'com.android.support.test:rules:0.4' 24 | androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2' 25 | } 26 | -------------------------------------------------------------------------------- /app-no-rx/src/androidTest/java/proxypref/test/TestActivityTest.java: -------------------------------------------------------------------------------- 1 | package proxypref.test; 2 | 3 | import android.content.SharedPreferences; 4 | import android.test.ActivityInstrumentationTestCase2; 5 | import android.test.UiThreadTest; 6 | 7 | import java.util.Set; 8 | 9 | import proxypref.ProxyPreferences; 10 | import proxypref.annotation.DefaultSet; 11 | import proxypref.annotation.DefaultString; 12 | import proxypref.annotation.Preference; 13 | 14 | public class TestActivityTest extends ActivityInstrumentationTestCase2 { 15 | 16 | interface MyPreferences { 17 | 18 | // access with get/set prefix 19 | String getTestString(); // key = testString 20 | void setTestString(String x); // key = testString 21 | 22 | // without get/set prefix 23 | Integer testInteger(); // key = testInteger 24 | void testInteger(Integer x); // key = testInteger 25 | 26 | // ProGuard ready 27 | @Preference("username") 28 | String a12(); // key = username 29 | 30 | // Default value 31 | @DefaultString("user256") 32 | String username(); // key = username 33 | 34 | // Default set 35 | @DefaultSet({"1", "2", "3"}) 36 | Set getSomeSet(); // key = someSet 37 | } 38 | 39 | public TestActivityTest() { 40 | super(TestActivity.class); 41 | } 42 | 43 | @Override 44 | public void setUp() throws Exception { 45 | super.setUp(); 46 | getActivity(); 47 | } 48 | 49 | @UiThreadTest 50 | public void testPreferences() throws Exception { 51 | SharedPreferences shared = getActivity().getSharedPreferences("1", 0); 52 | shared.edit().clear().apply(); 53 | MyPreferences pref = ProxyPreferences.build(MyPreferences.class, shared); 54 | 55 | assertNull(pref.getTestString()); 56 | pref.setTestString("123"); 57 | assertEquals("123", pref.getTestString()); 58 | assertEquals("123", shared.getString("testString", null)); 59 | 60 | assertEquals(null, pref.testInteger()); 61 | pref.testInteger(123); 62 | assertEquals((Integer)123, pref.testInteger()); 63 | assertEquals(123, shared.getInt("testInteger", 1)); 64 | } 65 | } -------------------------------------------------------------------------------- /app-no-rx/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app-no-rx/src/main/java/proxypref/test/TestActivity.java: -------------------------------------------------------------------------------- 1 | package proxypref.test; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class TestActivity extends Activity { 7 | @Override 8 | protected void onCreate(Bundle savedInstanceState) { 9 | super.onCreate(savedInstanceState); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app-no-rx/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/app-no-rx/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-no-rx/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/app-no-rx/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-no-rx/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/app-no-rx/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-no-rx/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/app-no-rx/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-no-rx/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ProxyPreferences 3 | 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "proxypref.test" 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | 15 | defaultConfig { 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | } 18 | } 19 | 20 | dependencies { 21 | compile project(':proxypref') 22 | compile 'io.reactivex:rxjava:1.0.14' 23 | androidTestCompile 'com.android.support.test:runner:0.4' 24 | androidTestCompile 'com.android.support.test:rules:0.4' 25 | androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2' 26 | } 27 | -------------------------------------------------------------------------------- /app/src/androidTest/java/proxypref/test/TestActivityTest.java: -------------------------------------------------------------------------------- 1 | package proxypref.test; 2 | 3 | import android.content.SharedPreferences; 4 | import android.test.ActivityInstrumentationTestCase2; 5 | import android.test.UiThreadTest; 6 | 7 | import java.util.Set; 8 | import java.util.concurrent.atomic.AtomicReference; 9 | 10 | import proxypref.ProxyPreferences; 11 | import proxypref.annotation.DefaultSet; 12 | import proxypref.annotation.DefaultString; 13 | import proxypref.annotation.Preference; 14 | import rx.Observable; 15 | import rx.functions.Action1; 16 | 17 | public class TestActivityTest extends ActivityInstrumentationTestCase2 { 18 | 19 | interface MyPreferences { 20 | 21 | // access with get/set prefix 22 | String getTestString(); // key = testString 23 | void setTestString(String x); // key = testString 24 | 25 | // without get/set prefix 26 | Integer testInteger(); // key = testInteger 27 | void testInteger(Integer x); // key = testInteger 28 | 29 | // observe with rx.Observable 30 | Observable lastSelectedItem(); // key = lastSelectedItem 31 | 32 | // set with rx.functions.Action1 33 | Action1 setLastSelectedItem(); // key = lastSelectedItem 34 | 35 | // ProGuard ready 36 | @Preference("username") 37 | String a12(); // key = username 38 | 39 | // Default value 40 | @DefaultString("user256") 41 | String username(); // key = username 42 | 43 | // Default set 44 | @DefaultSet({"1", "2", "3"}) 45 | Set getSomeSet(); // key = someSet 46 | } 47 | 48 | public TestActivityTest() { 49 | super(TestActivity.class); 50 | } 51 | 52 | @Override 53 | public void setUp() throws Exception { 54 | super.setUp(); 55 | getActivity(); 56 | } 57 | 58 | @UiThreadTest 59 | public void testPreferences() throws Exception { 60 | SharedPreferences shared = getActivity().getSharedPreferences("1", 0); 61 | shared.edit().clear().apply(); 62 | MyPreferences pref = ProxyPreferences.buildWithRx(MyPreferences.class, shared); 63 | 64 | assertNull(pref.getTestString()); 65 | pref.setTestString("123"); 66 | assertEquals("123", pref.getTestString()); 67 | assertEquals("123", shared.getString("testString", null)); 68 | 69 | assertEquals(null, pref.testInteger()); 70 | pref.testInteger(123); 71 | assertEquals((Integer)123, pref.testInteger()); 72 | assertEquals(123, shared.getInt("testInteger", 1)); 73 | 74 | final AtomicReference selected = new AtomicReference<>(); 75 | assertEquals(null, selected.get()); 76 | pref.lastSelectedItem().subscribe(new Action1() { 77 | @Override 78 | public void call(Integer integer) { 79 | selected.set(integer); 80 | } 81 | }); 82 | pref.setLastSelectedItem().call(123); 83 | assertEquals(123, (int)selected.get()); 84 | } 85 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/proxypref/test/TestActivity.java: -------------------------------------------------------------------------------- 1 | package proxypref.test; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class TestActivity extends Activity { 7 | @Override 8 | protected void onCreate(Bundle savedInstanceState) { 9 | super.onCreate(savedInstanceState); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ProxyPreferences 3 | 4 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | VERSION_CODE=2 2 | VERSION_NAME=0.2.0 3 | GROUP=info.android15.proxypref 4 | 5 | POM_DESCRIPTION=ProxyPref is a simple Android library, which allows to easily access SharedPreferences. 6 | POM_URL=https://github.com/konmik/proxypref 7 | POM_SCM_URL=https://github.com/konmik/proxypref 8 | POM_SCM_CONNECTION=scm:git@github.com:konmik/proxypref.git 9 | POM_SCM_DEV_CONNECTION=scm:git@github.com:konimk/proxypref.git 10 | POM_LICENCE_NAME=MIT 11 | POM_LICENCE_URL=http://opensource.org/licenses/MIT 12 | POM_LICENCE_DIST=repo 13 | POM_DEVELOPER_ID=sirstripy 14 | POM_DEVELOPER_NAME=Konstantin Mikheev 15 | 16 | #RELEASE_REPOSITORY_URL=file://C:/Users/konmik/.m2/repository 17 | #SNAPSHOT_REPOSITORY_URL=file://C:/Users/konmik/.m2/repository 18 | -------------------------------------------------------------------------------- /gradle/gradle-mvn-push.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | 20 | def isReleaseBuild() { 21 | return VERSION_NAME.contains("SNAPSHOT") == false 22 | } 23 | 24 | def getReleaseRepositoryUrl() { 25 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 26 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 27 | } 28 | 29 | def getSnapshotRepositoryUrl() { 30 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 31 | : "https://oss.sonatype.org/content/repositories/snapshots/" 32 | } 33 | 34 | def getRepositoryUsername() { 35 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 36 | } 37 | 38 | def getRepositoryPassword() { 39 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" 40 | } 41 | 42 | afterEvaluate { project -> 43 | uploadArchives { 44 | repositories { 45 | mavenDeployer { 46 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 47 | 48 | pom.groupId = GROUP 49 | pom.artifactId = POM_ARTIFACT_ID 50 | pom.version = VERSION_NAME 51 | 52 | repository(url: getReleaseRepositoryUrl()) { 53 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 54 | } 55 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 56 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 57 | } 58 | 59 | pom.project { 60 | name POM_NAME 61 | packaging POM_PACKAGING 62 | description POM_DESCRIPTION 63 | url POM_URL 64 | 65 | scm { 66 | url POM_SCM_URL 67 | connection POM_SCM_CONNECTION 68 | developerConnection POM_SCM_DEV_CONNECTION 69 | } 70 | 71 | licenses { 72 | license { 73 | name POM_LICENCE_NAME 74 | url POM_LICENCE_URL 75 | distribution POM_LICENCE_DIST 76 | } 77 | } 78 | 79 | developers { 80 | developer { 81 | id POM_DEVELOPER_ID 82 | name POM_DEVELOPER_NAME 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | signing { 91 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 92 | sign configurations.archives 93 | } 94 | 95 | task androidJavadocs(type: Javadoc) { 96 | failOnError false 97 | source = android.sourceSets.main.java.srcDirs 98 | classpath += project.files(android.getBootClasspath()) + project.files(configurations.compileJavadoc) 99 | } 100 | 101 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 102 | classifier = 'javadoc' 103 | from androidJavadocs.destinationDir 104 | } 105 | 106 | task androidSourcesJar(type: Jar) { 107 | classifier = 'sources' 108 | from android.sourceSets.main.java.sourceFiles 109 | } 110 | 111 | artifacts { 112 | archives androidSourcesJar 113 | archives androidJavadocsJar 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konmik/proxypref/e9096b089d0aab2c8930c0a6c51959d9e6455c49/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Sep 15 19:32:27 MSK 2015 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-2.4-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 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /proxypref/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /proxypref/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | } 14 | 15 | configurations { 16 | compileJavadoc 17 | } 18 | 19 | dependencies { 20 | compile "com.android.support:support-annotations:23.0.1" 21 | provided 'io.reactivex:rxjava:1.0.14' 22 | testCompile 'io.reactivex:rxjava:1.0.14' 23 | testCompile 'junit:junit:4.12' 24 | testCompile 'org.mockito:mockito-all:1.10.19' 25 | } 26 | 27 | android.libraryVariants.all { variant -> 28 | def name = variant.buildType.name 29 | if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) { 30 | return; // Skip debug builds. 31 | } 32 | def task = project.tasks.create "jar${name.capitalize()}", Jar 33 | task.dependsOn variant.javaCompile 34 | task.from variant.javaCompile.destinationDir 35 | artifacts.add('archives', task); 36 | } 37 | 38 | apply from: '../gradle/gradle-mvn-push.gradle' 39 | -------------------------------------------------------------------------------- /proxypref/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ProxyPref 2 | POM_ARTIFACT_ID=proxypref 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /proxypref/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/ProxyHandler.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import java.lang.reflect.InvocationHandler; 6 | import java.lang.reflect.Method; 7 | import java.util.HashMap; 8 | 9 | import proxypref.method.MethodInfo; 10 | 11 | class ProxyHandler implements InvocationHandler { 12 | 13 | private final SharedPreferences pref; 14 | private final boolean rx; 15 | 16 | public ProxyHandler(SharedPreferences pref, boolean rx) { 17 | this.pref = pref; 18 | this.rx = rx; 19 | } 20 | 21 | private static HashMap methods = new HashMap<>(); 22 | 23 | @Override 24 | public Object invoke(Object proxy, final Method method, Object[] args) throws Throwable { 25 | MethodInfo methodInfo = methods.get(method); 26 | if (methodInfo == null) 27 | methods.put(method, methodInfo = new MethodInfo(method, rx)); 28 | return methodInfo.invoke(pref, args); 29 | } 30 | 31 | public static void clearCache() { 32 | methods.clear(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/ProxyPreferences.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import java.lang.reflect.Proxy; 6 | 7 | public class ProxyPreferences { 8 | 9 | public static T build(Class tClass, SharedPreferences pref) { 10 | //noinspection unchecked 11 | return (T)Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new ProxyHandler(pref, false)); 12 | } 13 | 14 | public static T buildWithRx(Class tClass, SharedPreferences pref) { 15 | //noinspection unchecked 16 | return (T)Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new ProxyHandler(pref, true)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/annotation/DefaultBoolean.java: -------------------------------------------------------------------------------- 1 | package proxypref.annotation; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface DefaultBoolean { 8 | boolean value(); 9 | } 10 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/annotation/DefaultFloat.java: -------------------------------------------------------------------------------- 1 | package proxypref.annotation; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface DefaultFloat { 8 | float value(); 9 | } 10 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/annotation/DefaultInteger.java: -------------------------------------------------------------------------------- 1 | package proxypref.annotation; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface DefaultInteger { 8 | int value(); 9 | } 10 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/annotation/DefaultLong.java: -------------------------------------------------------------------------------- 1 | package proxypref.annotation; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface DefaultLong { 8 | long value(); 9 | } 10 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/annotation/DefaultSet.java: -------------------------------------------------------------------------------- 1 | package proxypref.annotation; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface DefaultSet { 8 | String[] value(); 9 | } 10 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/annotation/DefaultString.java: -------------------------------------------------------------------------------- 1 | package proxypref.annotation; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface DefaultString { 8 | String value(); 9 | } 10 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/annotation/Preference.java: -------------------------------------------------------------------------------- 1 | package proxypref.annotation; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface Preference { 8 | String value(); 9 | } 10 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/method/DataType.java: -------------------------------------------------------------------------------- 1 | package proxypref.method; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.ParameterizedType; 7 | import java.lang.reflect.Type; 8 | import java.util.Collections; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | import proxypref.annotation.DefaultBoolean; 13 | import proxypref.annotation.DefaultFloat; 14 | import proxypref.annotation.DefaultInteger; 15 | import proxypref.annotation.DefaultLong; 16 | import proxypref.annotation.DefaultSet; 17 | import proxypref.annotation.DefaultString; 18 | 19 | import static java.util.Arrays.asList; 20 | 21 | enum DataType { 22 | STRING { 23 | @Override 24 | void put(SharedPreferences pref, String key, Object value) { 25 | pref.edit().putString(key, (String)value).apply(); 26 | } 27 | 28 | @Override 29 | Object getDefaultValue(Method method) { 30 | DefaultString annotation = method.getAnnotation(DefaultString.class); 31 | return annotation == null ? null : annotation.value(); 32 | } 33 | }, 34 | INTEGER { 35 | @Override 36 | void put(SharedPreferences pref, String key, Object value) { 37 | pref.edit().putInt(key, (int)value).apply(); 38 | } 39 | 40 | @Override 41 | Object getDefaultValue(Method method) { 42 | DefaultInteger annotation = method.getAnnotation(DefaultInteger.class); 43 | return annotation == null ? null : annotation.value(); 44 | } 45 | }, 46 | LONG { 47 | @Override 48 | void put(SharedPreferences pref, String key, Object value) { 49 | pref.edit().putLong(key, (long)value).apply(); 50 | } 51 | 52 | @Override 53 | Object getDefaultValue(Method method) { 54 | DefaultLong annotation = method.getAnnotation(DefaultLong.class); 55 | return annotation == null ? null : annotation.value(); 56 | } 57 | }, 58 | FLOAT { 59 | @Override 60 | void put(SharedPreferences pref, String key, Object value) { 61 | pref.edit().putFloat(key, (float)value).apply(); 62 | } 63 | 64 | @Override 65 | Object getDefaultValue(Method method) { 66 | DefaultFloat annotation = method.getAnnotation(DefaultFloat.class); 67 | return annotation == null ? null : annotation.value(); 68 | } 69 | }, 70 | BOOLEAN { 71 | @Override 72 | void put(SharedPreferences pref, String key, Object value) { 73 | pref.edit().putBoolean(key, (boolean)value).apply(); 74 | } 75 | 76 | @Override 77 | Object getDefaultValue(Method method) { 78 | DefaultBoolean annotation = method.getAnnotation(DefaultBoolean.class); 79 | return annotation == null ? null : annotation.value(); 80 | } 81 | }, 82 | SET { 83 | @Override 84 | void put(SharedPreferences pref, String key, Object value) { 85 | pref.edit().putStringSet(key, (Set)value).apply(); 86 | } 87 | 88 | @Override 89 | Object getDefaultValue(Method method) { 90 | DefaultSet annotation = method.getAnnotation(DefaultSet.class); 91 | return annotation == null ? null : Collections.unmodifiableSet(new HashSet<>(asList(annotation.value()))); 92 | } 93 | }; 94 | 95 | static DataType fromClass(Class cls, Type type) { 96 | if (cls.equals(String.class)) 97 | return STRING; 98 | if (cls.equals(Integer.class)) 99 | return INTEGER; 100 | if (cls.equals(Long.class)) 101 | return LONG; 102 | if (cls.equals(Float.class)) 103 | return FLOAT; 104 | if (cls.equals(Boolean.class)) 105 | return BOOLEAN; 106 | if (cls.equals(Set.class) && type instanceof ParameterizedType && 107 | ((ParameterizedType)type).getActualTypeArguments()[0].equals(String.class)) 108 | return SET; 109 | throw new IllegalArgumentException("Invalid shared preferences type: " + type.toString()); 110 | } 111 | 112 | abstract void put(SharedPreferences pref, String key, Object value); 113 | abstract Object getDefaultValue(Method method); 114 | } 115 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/method/MethodInfo.java: -------------------------------------------------------------------------------- 1 | package proxypref.method; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | public class MethodInfo { 8 | 9 | private final MethodType methodType; 10 | private final DataType dataType; 11 | private final String key; 12 | private final Object defValue; 13 | 14 | public MethodInfo(Method method, boolean rx) { 15 | this.methodType = MethodType.from(method, rx); 16 | this.dataType = methodType.getDataType(method); 17 | this.key = methodType.getKey(method); 18 | this.defValue = dataType.getDefaultValue(method); 19 | } 20 | 21 | public Object invoke(SharedPreferences pref, Object[] args) { 22 | return methodType.invoke(dataType, key, pref, args, defValue); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/method/MethodType.java: -------------------------------------------------------------------------------- 1 | package proxypref.method; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.ParameterizedType; 7 | import java.lang.reflect.Type; 8 | 9 | import proxypref.annotation.Preference; 10 | import rx.Observable; 11 | import rx.functions.Action1; 12 | 13 | enum MethodType { 14 | 15 | GET(false) { 16 | @Override 17 | DataType getDataType(Method method) { 18 | return DataType.fromClass(method.getReturnType(), method.getGenericReturnType()); 19 | } 20 | 21 | @Override 22 | public Object invoke(DataType dataType, String key, SharedPreferences pref, Object[] args, Object defValue) { 23 | Object value = pref.getAll().get(key); 24 | return value == null ? defValue : value; 25 | } 26 | }, 27 | SET(true) { 28 | @Override 29 | DataType getDataType(Method method) { 30 | return DataType.fromClass(method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); 31 | } 32 | 33 | @Override 34 | public Object invoke(DataType dataType, String key, SharedPreferences pref, Object[] args, Object defValue) { 35 | if (args[0] == null) 36 | pref.edit().remove(key).apply(); 37 | else 38 | dataType.put(pref, key, args[0]); 39 | return null; 40 | } 41 | }, 42 | OBSERVABLE(false) { 43 | @Override 44 | DataType getDataType(Method method) { 45 | return dataTypeFromGenericReturnType(method); 46 | } 47 | 48 | @Override 49 | public Object invoke(final DataType dataType, final String key, final SharedPreferences pref, final Object[] args, final Object defValue) { 50 | return RxDelegate.createObservable(dataType, key, pref, args, defValue); 51 | } 52 | }, 53 | ACTION(true) { 54 | @Override 55 | DataType getDataType(Method method) { 56 | return dataTypeFromGenericReturnType(method); 57 | } 58 | 59 | @Override 60 | public Object invoke(final DataType dataType, final String key, final SharedPreferences pref, final Object[] args, final Object defValue) { 61 | return RxDelegate.createAction1(dataType, key, pref, defValue); 62 | } 63 | }; 64 | 65 | static MethodType from(Method method, boolean rx) { 66 | Class returnType = method.getReturnType(); 67 | boolean returnVoid = returnType.equals(Void.TYPE); 68 | int parameterCount = method.getParameterTypes().length; 69 | if (returnVoid && parameterCount == 1) 70 | return MethodType.SET; 71 | if (!returnVoid && parameterCount == 0) { 72 | if (rx) { 73 | if (returnType.equals(Action1.class)) 74 | return MethodType.ACTION; 75 | if (returnType.equals(Observable.class)) 76 | return MethodType.OBSERVABLE; 77 | } 78 | return MethodType.GET; 79 | } 80 | throw illegalMethodException(method, "Unable to detect a method type"); 81 | } 82 | 83 | private static DataType dataTypeFromGenericReturnType(Method method) { 84 | Type returnType = method.getGenericReturnType(); 85 | if (!(returnType instanceof ParameterizedType)) 86 | throw illegalMethodException(method, "Invalid shared preferences type"); 87 | ParameterizedType parameterizedType = (ParameterizedType)returnType; 88 | Type arg0 = parameterizedType.getActualTypeArguments()[0]; 89 | return DataType.fromClass((Class)(arg0 instanceof Class ? arg0 : ((ParameterizedType)arg0).getRawType()), arg0); 90 | } 91 | 92 | private final boolean isSet; 93 | 94 | MethodType(boolean isSet) { 95 | this.isSet = isSet; 96 | } 97 | 98 | abstract DataType getDataType(Method method); 99 | abstract Object invoke(DataType dataType, String key, SharedPreferences pref, Object[] args, Object defValue); 100 | 101 | String getKey(Method method) { 102 | Preference preference = method.getAnnotation(Preference.class); 103 | if (preference != null) 104 | return preference.value(); 105 | 106 | String name = method.getName(); 107 | return (name.length() > 3 && name.startsWith(isSet ? "set" : "get")) ? 108 | name.substring(3, 4).toLowerCase() + (name.length() > 4 ? name.substring(4) : "") : 109 | name; 110 | } 111 | 112 | private static IllegalArgumentException illegalMethodException(Method method, String error) { 113 | throw new IllegalArgumentException("Method: " + method.getDeclaringClass().getName() + "." + method.getName() + "\n" + error); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/method/OnSharedPreferenceChangeListenerOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package proxypref.method; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import rx.Observable; 6 | import rx.Subscriber; 7 | import rx.functions.Action0; 8 | import rx.functions.Func0; 9 | import rx.subscriptions.Subscriptions; 10 | 11 | public class OnSharedPreferenceChangeListenerOnSubscribe implements Observable.OnSubscribe { 12 | 13 | private final SharedPreferences pref; 14 | private final String name; 15 | private final Func0 get; 16 | 17 | public OnSharedPreferenceChangeListenerOnSubscribe(SharedPreferences pref, String name, Func0 get) { 18 | this.pref = pref; 19 | this.name = name; 20 | this.get = get; 21 | } 22 | 23 | @Override 24 | public void call(final Subscriber subscriber) { 25 | final SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() { 26 | @Override 27 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 28 | if (key.equals(name)) 29 | subscriber.onNext(get.call()); 30 | } 31 | }; 32 | subscriber.add(Subscriptions.create(new Action0() { 33 | @Override 34 | public void call() { 35 | pref.unregisterOnSharedPreferenceChangeListener(listener); 36 | } 37 | })); 38 | pref.registerOnSharedPreferenceChangeListener(listener); 39 | 40 | subscriber.onNext(get.call()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /proxypref/src/main/java/proxypref/method/RxDelegate.java: -------------------------------------------------------------------------------- 1 | package proxypref.method; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import rx.Observable; 6 | import rx.functions.Action1; 7 | import rx.functions.Func0; 8 | 9 | class RxDelegate { 10 | 11 | static Object createAction1(final DataType dataType, final String key, final SharedPreferences pref, final Object defValue) { 12 | return new Action1() { 13 | @Override 14 | public void call(Object o) { 15 | MethodType.SET.invoke(dataType, key, pref, new Object[]{o}, defValue); 16 | } 17 | }; 18 | } 19 | 20 | static Object createObservable(final DataType dataType, final String key, final SharedPreferences pref, final Object[] args, final Object defValue) { 21 | return Observable.create(new OnSharedPreferenceChangeListenerOnSubscribe(pref, key, new Func0() { 22 | @Override 23 | public Object call() { 24 | return MethodType.GET.invoke(dataType, key, pref, args, defValue); 25 | } 26 | })); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/Coverage.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import org.junit.Test; 4 | 5 | import info.android15.proxypreferences.BuildConfig; 6 | 7 | public class Coverage { 8 | @Test 9 | public void instantiate_utility_classes() throws Exception { 10 | new ProxyPreferences(); 11 | new BuildConfig(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/DefaultValuesTest.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import org.junit.Test; 6 | import org.mockito.Mockito; 7 | 8 | import java.util.Set; 9 | 10 | import proxypref.annotation.DefaultBoolean; 11 | import proxypref.annotation.DefaultFloat; 12 | import proxypref.annotation.DefaultInteger; 13 | import proxypref.annotation.DefaultLong; 14 | import proxypref.annotation.DefaultSet; 15 | import proxypref.annotation.DefaultString; 16 | 17 | import static org.junit.Assert.assertArrayEquals; 18 | import static org.junit.Assert.assertEquals; 19 | 20 | public class DefaultValuesTest { 21 | 22 | interface Defaults { 23 | 24 | @DefaultString("value") 25 | String getString(); 26 | 27 | @DefaultInteger(64) 28 | Integer getInteger(); 29 | 30 | @DefaultLong(64) 31 | Long getLong(); 32 | 33 | @DefaultFloat(64f) 34 | Float getFloat(); 35 | 36 | @DefaultBoolean(true) 37 | Boolean getBoolean(); 38 | 39 | @DefaultSet({"value"}) 40 | Set getSet(); 41 | } 42 | 43 | @Test 44 | public void default_values_returned() throws Exception { 45 | SharedPreferences pref = Mockito.mock(SharedPreferences.class); 46 | Defaults test = ProxyPreferences.build(Defaults.class, pref); 47 | 48 | assertEquals("value", test.getString()); 49 | assertEquals(64, (int)test.getInteger()); 50 | assertEquals(64, (long)test.getLong()); 51 | assertEquals(64, test.getFloat(), 0); 52 | assertEquals(true, test.getBoolean()); 53 | assertArrayEquals(new Object[]{"value"}, test.getSet().toArray()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/ProxyHandlerAssertionsTest.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | 9 | import java.util.Set; 10 | 11 | public class ProxyHandlerAssertionsTest { 12 | 13 | interface TotallyFailed { 14 | void no_arg_void_return(); 15 | Integer arg_and_return(Integer x); 16 | void two_args(Integer x, Integer y); 17 | 18 | Double illegal_return_type(); 19 | void illegal_arg_type(Double x); 20 | 21 | Set not_parametrized_return_set_type(); 22 | void not_parametrized_argument_set_type(Set x); 23 | Set illegal_return_set_type(); 24 | void illegal_argument_set_type(Set x); 25 | } 26 | 27 | @Rule public ExpectedException expected = ExpectedException.none(); 28 | 29 | TotallyFailed test; 30 | 31 | @Before 32 | public void setUp() throws Exception { 33 | test = ProxyPreferences.build(TotallyFailed.class, null); 34 | } 35 | 36 | @After 37 | public void tearDown() throws Exception { 38 | ProxyHandler.clearCache(); 39 | } 40 | 41 | @Test 42 | public void no_arg_void_return_throws() throws Exception { 43 | expect("Unable to detect a method type"); 44 | test.no_arg_void_return(); 45 | } 46 | 47 | @Test 48 | public void arg_and_return_throws() throws Exception { 49 | expect("Unable to detect a method type"); 50 | test.arg_and_return(1); 51 | } 52 | 53 | @Test 54 | public void two_args_throws() throws Exception { 55 | expect("Unable to detect a method type"); 56 | test.two_args(1, 1); 57 | } 58 | 59 | @Test 60 | public void illegal_return_type_throws() throws Exception { 61 | expect("Invalid shared preferences type"); 62 | test.illegal_return_type(); 63 | } 64 | 65 | @Test 66 | public void illegal_arg_type_throws() throws Exception { 67 | expect("Invalid shared preferences type"); 68 | test.illegal_arg_type(1d); 69 | } 70 | 71 | @Test 72 | public void not_parametrized_return_set_type() throws Exception { 73 | expect("Invalid shared preferences type"); 74 | test.not_parametrized_return_set_type(); 75 | } 76 | 77 | @Test 78 | public void not_parametrized_argument_set_type() throws Exception { 79 | expect("Invalid shared preferences type"); 80 | test.not_parametrized_argument_set_type(null); 81 | } 82 | 83 | @Test 84 | public void illegal_return_set_type_throws() throws Exception { 85 | expect("Invalid shared preferences type"); 86 | test.illegal_return_set_type(); 87 | } 88 | 89 | @Test 90 | public void illegal_argument_set_type_throws() throws Exception { 91 | expect("Invalid shared preferences type"); 92 | test.illegal_argument_set_type(null); 93 | } 94 | 95 | private void expect(String substring) { 96 | expected.expect(IllegalArgumentException.class); 97 | expected.expectMessage(substring); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/ProxyHandlerKeyTest.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import org.junit.Test; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.Map; 9 | 10 | import proxypref.annotation.Preference; 11 | 12 | import static org.mockito.Matchers.anyString; 13 | import static org.mockito.Matchers.eq; 14 | import static org.mockito.Mockito.mock; 15 | import static org.mockito.Mockito.times; 16 | import static org.mockito.Mockito.verify; 17 | import static org.mockito.Mockito.when; 18 | 19 | public class ProxyHandlerKeyTest { 20 | 21 | interface NameTests { 22 | String g(); 23 | String get(); 24 | String getALLBIG(); 25 | String methodGet2(); 26 | @Preference("customName1") 27 | String methodCustom1(); 28 | 29 | void setCamelCase(String a); 30 | void methodSet2(String a); 31 | @Preference("customName2") 32 | void methodCustom2(String a); 33 | } 34 | 35 | @Test 36 | public void testName() throws Throwable { 37 | verifyGetMethodToKey("g", "g"); 38 | verifyGetMethodToKey("get", "get"); 39 | verifyGetMethodToKey("getALLBIG", "aLLBIG"); 40 | verifyGetMethodToKey("methodGet2", "methodGet2"); 41 | verifyGetMethodToKey("methodCustom1", "customName1"); 42 | 43 | verifyPutMethodToKey("setCamelCase", "camelCase"); 44 | verifyPutMethodToKey("methodSet2", "methodSet2"); 45 | verifyPutMethodToKey("methodCustom2", "customName2"); 46 | } 47 | 48 | private void verifyGetMethodToKey(String methodName, String expectedKey) throws Throwable { 49 | SharedPreferences pref = mock(SharedPreferences.class); 50 | Map map = mock(Map.class); 51 | when(pref.getAll()).thenReturn(map); 52 | Method method = NameTests.class.getDeclaredMethod(methodName); 53 | new ProxyHandler(pref, false).invoke(NameTests.class, method, new Object[0]); 54 | verify(map).get(expectedKey); 55 | } 56 | 57 | private void verifyPutMethodToKey(String methodName, String expectedKey) throws Throwable { 58 | SharedPreferences pref = mock(SharedPreferences.class); 59 | Method method = NameTests.class.getDeclaredMethod(methodName, String.class); 60 | 61 | SharedPreferences.Editor editor = TestUtil.mockEditor(pref); 62 | 63 | new ProxyHandler(pref, false).invoke(NameTests.class, method, new Object[]{""}); 64 | 65 | verify(editor, times(1)).putString(eq(expectedKey), anyString()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/ProxyHandlerRxAssertionsTest.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | 9 | import java.util.Set; 10 | 11 | import rx.Observable; 12 | import rx.functions.Action1; 13 | 14 | public class ProxyHandlerRxAssertionsTest { 15 | 16 | interface TotallyFailed { 17 | Observable illegal_observable_type(); 18 | Observable> illegal_observable_set_type(); 19 | Observable not_parametrized_observable_set_type(); 20 | 21 | Action1 no_action_type(); 22 | Action1 illegal_action_type(); 23 | Action1> illegal_action_set_type(); 24 | Action1 not_parametrized_action_set_type(); 25 | } 26 | 27 | @Rule public ExpectedException expected = ExpectedException.none(); 28 | 29 | TotallyFailed test; 30 | 31 | @Before 32 | public void setUp() throws Exception { 33 | test = ProxyPreferences.buildWithRx(TotallyFailed.class, null); 34 | } 35 | 36 | @After 37 | public void tearDown() throws Exception { 38 | ProxyHandler.clearCache(); 39 | } 40 | 41 | @Test 42 | public void illegal_observable_type() throws Exception { 43 | expect("Invalid shared preferences type"); 44 | test.illegal_observable_type(); 45 | } 46 | 47 | @Test 48 | public void illegal_observable_set_type() throws Exception { 49 | expect("Invalid shared preferences type"); 50 | test.illegal_observable_set_type(); 51 | } 52 | 53 | @Test 54 | public void not_parametrized_observable_set_type() throws Exception { 55 | expect("Invalid shared preferences type"); 56 | test.not_parametrized_observable_set_type(); 57 | } 58 | 59 | @Test 60 | public void no_action_type() throws Exception { 61 | expect("Invalid shared preferences type"); 62 | test.no_action_type(); 63 | } 64 | 65 | @Test 66 | public void illegal_action_type() throws Exception { 67 | expect("Invalid shared preferences type"); 68 | test.illegal_action_type(); 69 | } 70 | 71 | @Test 72 | public void illegal_action_set_type() throws Exception { 73 | expect("Invalid shared preferences type"); 74 | test.illegal_action_set_type(); 75 | } 76 | 77 | @Test 78 | public void not_parametrized_action_set_type() throws Exception { 79 | expect("Invalid shared preferences type"); 80 | test.not_parametrized_action_set_type(); 81 | } 82 | 83 | private void expect(String substring) { 84 | expected.expect(IllegalArgumentException.class); 85 | expected.expectMessage(substring); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/ProxyHandlerRxTest.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.mockito.InOrder; 9 | import org.mockito.Mockito; 10 | import org.mockito.invocation.InvocationOnMock; 11 | import org.mockito.stubbing.Answer; 12 | 13 | import java.util.Collections; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.concurrent.atomic.AtomicReference; 17 | 18 | import rx.Observable; 19 | import rx.Subscription; 20 | import rx.functions.Action1; 21 | import rx.observers.TestSubscriber; 22 | 23 | import static java.util.Arrays.asList; 24 | import static java.util.Collections.singletonList; 25 | import static org.junit.Assert.assertNotNull; 26 | import static org.mockito.Matchers.any; 27 | import static org.mockito.Mockito.doAnswer; 28 | import static org.mockito.Mockito.inOrder; 29 | import static org.mockito.Mockito.times; 30 | import static org.mockito.Mockito.verify; 31 | import static org.mockito.Mockito.when; 32 | import static proxypref.TestUtil.mockMapValue; 33 | 34 | public class ProxyHandlerRxTest { 35 | 36 | interface StringTest { 37 | Action1 action(); 38 | Observable observable(); 39 | } 40 | 41 | SharedPreferences pref; 42 | StringTest test; 43 | 44 | @Before 45 | public void setUp() throws Exception { 46 | pref = Mockito.mock(SharedPreferences.class); 47 | test = ProxyPreferences.buildWithRx(StringTest.class, pref); 48 | } 49 | 50 | @After 51 | public void tearDown() throws Exception { 52 | ProxyHandler.clearCache(); 53 | } 54 | 55 | @Test 56 | public void action_sets_value() throws Exception { 57 | SharedPreferences.Editor editor = TestUtil.mockEditor(pref); 58 | 59 | test.action().call("value"); 60 | 61 | InOrder order = inOrder(pref, editor); 62 | order.verify(pref, times(1)).edit(); 63 | order.verify(editor, times(1)).putString("action", "value"); 64 | order.verify(editor, times(1)).apply(); 65 | } 66 | 67 | @Test 68 | public void observable_returns_an_existing_value_immediately() throws Exception { 69 | Map map = mockMapValue("observable", "value1"); 70 | when(pref.getAll()).thenReturn(map); 71 | 72 | TestSubscriber subscriber = new TestSubscriber<>(); 73 | test.observable().subscribe(subscriber); 74 | subscriber.assertReceivedOnNext(singletonList("value1")); 75 | } 76 | 77 | @Test 78 | public void observable_returns_the_next_value_on_update() throws Exception { 79 | Map map = mockMapValue("observable", "value1"); 80 | when(pref.getAll()).thenReturn(map); 81 | 82 | final AtomicReference register = new AtomicReference<>(); 83 | doAnswer(new Answer() { 84 | @Override 85 | public Object answer(InvocationOnMock invocation) throws Throwable { 86 | register.set((SharedPreferences.OnSharedPreferenceChangeListener)invocation.getArguments()[0]); 87 | return null; 88 | } 89 | }).when(pref).registerOnSharedPreferenceChangeListener(any(SharedPreferences.OnSharedPreferenceChangeListener.class)); 90 | 91 | TestSubscriber subscriber = new TestSubscriber<>(); 92 | test.observable().subscribe(subscriber); 93 | 94 | map = mockMapValue("observable", "value2"); 95 | when(pref.getAll()).thenReturn(map); 96 | register.get().onSharedPreferenceChanged(pref, "observable"); 97 | 98 | subscriber.assertReceivedOnNext(asList("value1", "value2")); 99 | } 100 | 101 | @Test 102 | public void observable_unregisters_on_unsubscribe() throws Exception { 103 | final AtomicReference unregister = new AtomicReference<>(); 104 | doAnswer(new Answer() { 105 | @Override 106 | public Object answer(InvocationOnMock invocation) throws Throwable { 107 | unregister.set((SharedPreferences.OnSharedPreferenceChangeListener)invocation.getArguments()[0]); 108 | return null; 109 | } 110 | }).when(pref).unregisterOnSharedPreferenceChangeListener(any(SharedPreferences.OnSharedPreferenceChangeListener.class)); 111 | 112 | Subscription subscription = test.observable().subscribe(); 113 | 114 | subscription.unsubscribe(); 115 | assertNotNull(unregister.get()); 116 | } 117 | 118 | @Test 119 | public void observable_returns_null_if_no_data() throws Exception { 120 | Map map = new HashMap(); 121 | when(pref.getAll()).thenReturn(map); 122 | 123 | TestSubscriber subscriber = new TestSubscriber<>(); 124 | test.observable().subscribe(subscriber); 125 | subscriber.assertReceivedOnNext(Collections.singletonList(null)); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/ProxyHandlerTest.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.mockito.InOrder; 9 | import org.mockito.Mockito; 10 | 11 | import java.util.Map; 12 | 13 | import proxypref.annotation.DefaultString; 14 | import proxypref.annotation.Preference; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertNull; 18 | import static org.mockito.Mockito.inOrder; 19 | import static org.mockito.Mockito.mock; 20 | import static org.mockito.Mockito.times; 21 | import static org.mockito.Mockito.verify; 22 | import static org.mockito.Mockito.when; 23 | 24 | public class ProxyHandlerTest { 25 | 26 | interface StringTest { 27 | String test(); 28 | @DefaultString("defValue") 29 | String getTest(); 30 | void test(String value); 31 | @Preference("test") 32 | String getStrictName(); 33 | } 34 | 35 | SharedPreferences pref; 36 | StringTest test; 37 | 38 | @Before 39 | public void setUp() throws Exception { 40 | pref = Mockito.mock(SharedPreferences.class); 41 | test = ProxyPreferences.build(StringTest.class, pref); 42 | } 43 | 44 | @After 45 | public void tearDown() throws Exception { 46 | ProxyHandler.clearCache(); 47 | } 48 | 49 | @Test 50 | public void get_returns_value_from_preferences() throws Exception { 51 | when(pref.getAll()) 52 | .thenAnswer(TestUtil.answerMapValue("test", "value")); 53 | assertEquals("value", test.test()); 54 | } 55 | 56 | @Test 57 | public void get_returns_null_when_empty_preference() throws Exception { 58 | assertNull(test.test()); 59 | } 60 | 61 | @Test 62 | public void get_returns_default_value_when_empty_preference_and_default_value_is_set() throws Exception { 63 | assertEquals("defValue", test.getTest()); 64 | } 65 | 66 | @Test 67 | public void set_puts_data_into_preference() throws Exception { 68 | SharedPreferences.Editor editor = TestUtil.mockEditor(pref); 69 | 70 | test.test("value"); 71 | 72 | InOrder order = inOrder(pref, editor); 73 | order.verify(pref, times(1)).edit(); 74 | order.verify(editor, times(1)).putString("test", "value"); 75 | order.verify(editor, times(1)).apply(); 76 | } 77 | 78 | @Test 79 | public void set_null_removes_value() throws Exception { 80 | SharedPreferences.Editor editor = TestUtil.mockEditor(pref); 81 | 82 | test.test(null); 83 | 84 | InOrder order = inOrder(pref, editor); 85 | order.verify(pref, times(1)).edit(); 86 | order.verify(editor, times(1)).remove("test"); 87 | order.verify(editor, times(1)).apply(); 88 | } 89 | 90 | @Test 91 | public void key_is_being_taken_from_Preference_annotation() throws Exception { 92 | Map map = mock(Map.class); 93 | when(pref.getAll()).thenReturn(map); 94 | test.getStrictName(); 95 | verify(map).get("test"); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/ProxyHandlerTypesTest.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import org.junit.Test; 6 | import org.mockito.InOrder; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | import rx.functions.Action2; 12 | import rx.functions.Func1; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static org.mockito.Matchers.any; 16 | import static org.mockito.Matchers.anyBoolean; 17 | import static org.mockito.Matchers.anyFloat; 18 | import static org.mockito.Matchers.anyInt; 19 | import static org.mockito.Matchers.anyLong; 20 | import static org.mockito.Matchers.anyString; 21 | import static org.mockito.Mockito.inOrder; 22 | import static org.mockito.Mockito.mock; 23 | import static org.mockito.Mockito.times; 24 | import static org.mockito.Mockito.when; 25 | import static proxypref.TestUtil.answerMapValue; 26 | 27 | public class ProxyHandlerTypesTest { 28 | 29 | interface TestTypes { 30 | String getString(); 31 | void setString(String x); 32 | Integer getInt(); 33 | void setInt(Integer x); 34 | Long getLong(); 35 | void setLong(Long x); 36 | Float getFloat(); 37 | void setFloat(Float x); 38 | Boolean getBoolean(); 39 | void setBoolean(Boolean x); 40 | Set set(); 41 | void set(Set x); 42 | } 43 | 44 | @Test 45 | public void testTypes() throws Exception { 46 | testType("string", "setValue", "getValue", 47 | new Func1() { 48 | @Override 49 | public SharedPreferences.Editor call(SharedPreferences.Editor editor) { 50 | return editor.putString(anyString(), anyString()); 51 | } 52 | }, 53 | new Action2() { 54 | @Override 55 | public void call(SharedPreferences.Editor editor, String value) { 56 | editor.putString("string", value); 57 | } 58 | }, 59 | new Func1() { 60 | @Override 61 | public String call(TestTypes testTypes) { 62 | return testTypes.getString(); 63 | } 64 | }, 65 | new Action2() { 66 | @Override 67 | public void call(TestTypes testTypes, String s) { 68 | testTypes.setString(s); 69 | } 70 | }); 71 | testType("int", 1, 2, 72 | new Func1() { 73 | @Override 74 | public SharedPreferences.Editor call(SharedPreferences.Editor editor) { 75 | return editor.putInt(anyString(), anyInt()); 76 | } 77 | }, 78 | new Action2() { 79 | @Override 80 | public void call(SharedPreferences.Editor editor, Integer value) { 81 | editor.putInt("int", value); 82 | } 83 | }, 84 | new Func1() { 85 | @Override 86 | public Integer call(TestTypes testTypes) { 87 | return testTypes.getInt(); 88 | } 89 | }, 90 | new Action2() { 91 | @Override 92 | public void call(TestTypes testTypes, Integer s) { 93 | testTypes.setInt(s); 94 | } 95 | }); 96 | testType("long", 1l, 2l, 97 | new Func1() { 98 | @Override 99 | public SharedPreferences.Editor call(SharedPreferences.Editor editor) { 100 | return editor.putLong(anyString(), anyLong()); 101 | } 102 | }, 103 | new Action2() { 104 | @Override 105 | public void call(SharedPreferences.Editor editor, Long value) { 106 | editor.putLong("long", value); 107 | } 108 | }, 109 | new Func1() { 110 | @Override 111 | public Long call(TestTypes testTypes) { 112 | return testTypes.getLong(); 113 | } 114 | }, 115 | new Action2() { 116 | @Override 117 | public void call(TestTypes testTypes, Long s) { 118 | testTypes.setLong(s); 119 | } 120 | }); 121 | testType("float", 1f, 2f, 122 | new Func1() { 123 | @Override 124 | public SharedPreferences.Editor call(SharedPreferences.Editor editor) { 125 | return editor.putFloat(anyString(), anyFloat()); 126 | } 127 | }, 128 | new Action2() { 129 | @Override 130 | public void call(SharedPreferences.Editor editor, Float value) { 131 | editor.putFloat("float", value); 132 | } 133 | }, 134 | new Func1() { 135 | @Override 136 | public Float call(TestTypes testTypes) { 137 | return testTypes.getFloat(); 138 | } 139 | }, 140 | new Action2() { 141 | @Override 142 | public void call(TestTypes testTypes, Float s) { 143 | testTypes.setFloat(s); 144 | } 145 | }); 146 | testType("boolean", true, false, 147 | new Func1() { 148 | @Override 149 | public SharedPreferences.Editor call(SharedPreferences.Editor editor) { 150 | return editor.putBoolean(anyString(), anyBoolean()); 151 | } 152 | }, 153 | new Action2() { 154 | @Override 155 | public void call(SharedPreferences.Editor editor, Boolean value) { 156 | editor.putBoolean("boolean", value); 157 | } 158 | }, 159 | new Func1() { 160 | @Override 161 | public Boolean call(TestTypes testTypes) { 162 | return testTypes.getBoolean(); 163 | } 164 | }, 165 | new Action2() { 166 | @Override 167 | public void call(TestTypes testTypes, Boolean s) { 168 | testTypes.setBoolean(s); 169 | } 170 | }); 171 | testType("set", new HashSet(), new HashSet(), 172 | new Func1() { 173 | @Override 174 | public SharedPreferences.Editor call(SharedPreferences.Editor editor) { 175 | return editor.putStringSet(anyString(), any(Set.class)); 176 | } 177 | }, 178 | new Action2>() { 179 | @Override 180 | public void call(SharedPreferences.Editor editor, Set value) { 181 | editor.putStringSet("set", value); 182 | } 183 | }, 184 | new Func1>() { 185 | @Override 186 | public Set call(TestTypes testTypes) { 187 | return testTypes.set(); 188 | } 189 | }, 190 | new Action2>() { 191 | @Override 192 | public void call(TestTypes testTypes, Set s) { 193 | testTypes.set(s); 194 | } 195 | }); 196 | } 197 | 198 | private void testType(String methodName, T getValue, T setValue, 199 | Func1 whenPut, 200 | Action2 verifyPut, 201 | Func1 doGet, Action2 doSet) { 202 | 203 | SharedPreferences pref = mock(SharedPreferences.class); 204 | TestTypes test = ProxyPreferences.build(TestTypes.class, pref); 205 | 206 | when(pref.getAll()) 207 | .thenAnswer(answerMapValue(methodName, getValue)); 208 | 209 | assertEquals(getValue, doGet.call(test)); 210 | 211 | SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class); 212 | when(pref.edit()) 213 | .thenReturn(editor); 214 | when(whenPut.call(editor)) 215 | .thenReturn(editor); 216 | 217 | doSet.call(test, setValue); 218 | 219 | InOrder order = inOrder(pref, editor); 220 | order.verify(pref, times(1)).edit(); 221 | verifyPut.call(order.verify(editor, times(1)), setValue); 222 | order.verify(editor, times(1)).apply(); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/TestUtil.java: -------------------------------------------------------------------------------- 1 | package proxypref; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import org.mockito.invocation.InvocationOnMock; 6 | import org.mockito.stubbing.Answer; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | import static org.mockito.Matchers.any; 13 | import static org.mockito.Matchers.anyBoolean; 14 | import static org.mockito.Matchers.anyFloat; 15 | import static org.mockito.Matchers.anyInt; 16 | import static org.mockito.Matchers.anyLong; 17 | import static org.mockito.Matchers.anyString; 18 | import static org.mockito.Matchers.eq; 19 | import static org.mockito.Mockito.doReturn; 20 | import static org.mockito.Mockito.mock; 21 | import static org.mockito.Mockito.when; 22 | 23 | public class TestUtil { 24 | static Answer answerDefault(final String name) { 25 | return new Answer() { 26 | @Override 27 | public T answer(InvocationOnMock invocationOnMock) throws Throwable { 28 | return name.equals(invocationOnMock.getArguments()[0]) ? (T)invocationOnMock.getArguments()[1] : null; 29 | } 30 | }; 31 | } 32 | 33 | static Answer answerMapValue(final String name, final Object value) { 34 | return new Answer() { 35 | @Override 36 | public Map answer(InvocationOnMock invocationOnMock) throws Throwable { 37 | return new HashMap() {{ 38 | put(name, value); 39 | }}; 40 | } 41 | }; 42 | } 43 | 44 | static SharedPreferences.Editor mockEditor(SharedPreferences prefMock) { 45 | SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class); 46 | 47 | when(editor.putString(anyString(), anyString())).thenReturn(editor); 48 | when(editor.putInt(anyString(), anyInt())).thenReturn(editor); 49 | when(editor.putLong(anyString(), anyLong())).thenReturn(editor); 50 | when(editor.putFloat(anyString(), anyFloat())).thenReturn(editor); 51 | when(editor.putBoolean(anyString(), anyBoolean())).thenReturn(editor); 52 | when(editor.putStringSet(anyString(), any(Set.class))).thenReturn(editor); 53 | 54 | when(editor.clear()).thenReturn(editor); 55 | when(editor.remove(anyString())).thenReturn(editor); 56 | doReturn(true).when(editor).commit(); 57 | 58 | when(prefMock.edit()).thenReturn(editor); 59 | return editor; 60 | } 61 | 62 | static Map mockMapValue(String key, Object value) { 63 | Map map = mock(Map.class); 64 | when(map.get(eq(key))).thenReturn(value); 65 | return map; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /proxypref/src/test/java/proxypref/method/Coverage.java: -------------------------------------------------------------------------------- 1 | package proxypref.method; 2 | 3 | import org.junit.Test; 4 | 5 | public class Coverage { 6 | @Test 7 | public void instantiate_utility_classes() throws Exception { 8 | new RxDelegate(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':proxypref', ':app', ':app-no-rx' 2 | --------------------------------------------------------------------------------