├── sample ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── ivanshafran │ │ │ └── sharedpreferencesmock │ │ │ └── sample │ │ │ ├── ShowMessageLogic.java │ │ │ └── CounterPreferences.java │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── ivanshafran │ │ └── sharedpreferencesmock │ │ └── sample │ │ └── ShowMessageLogicTest.java ├── proguard-rules.pro └── build.gradle ├── sharedpreferencesmock ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── ivanshafran │ │ │ └── sharedpreferencesmock │ │ │ ├── internal │ │ │ ├── SharedPreferencesFactory.java │ │ │ ├── SPContextWrapperMock.java │ │ │ ├── ThreadSafeSharedPreferencesMock.java │ │ │ ├── SharedPreferencesMock.java │ │ │ └── ContextMock.java │ │ │ └── SPMockBuilder.java │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── ivanshafran │ │ └── sharedpreferencesmock │ │ ├── internal │ │ └── SharedPreferencesFactoryTest.kt │ │ ├── SPMockBuilderTest.kt │ │ ├── SharedPreferencesDeleteTest.kt │ │ ├── SharedPreferencesStringSetTest.kt │ │ ├── SharedPreferencesDefaultValuesTest.kt │ │ ├── SPContextWrapperMockSharedPreferencesTest.kt │ │ ├── SharedPreferencesPutGetTests.kt │ │ ├── ContextMockThrowsTest.kt │ │ └── SharedPreferencesListenersTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .github └── workflows │ ├── code-health-check.yml │ └── publish.yml ├── gradle.properties ├── LICENSE ├── scripts ├── publish-root.gradle └── publish-module.gradle ├── gradlew.bat ├── gradlew └── README.MD /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sharedpreferencesmock/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample', ':sharedpreferencesmock' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SharedPreferencesMock 3 | 4 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SharePreferencesMock 3 | 4 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanShafran/shared-preferences-mock/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sharedpreferencesmock/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Aug 12 10:03:11 MDT 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.1.1-all.zip 7 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/java/com/github/ivanshafran/sharedpreferencesmock/sample/ShowMessageLogic.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.sample; 2 | 3 | public class ShowMessageLogic { 4 | 5 | private final CounterPreferences counterPreferences; 6 | 7 | public ShowMessageLogic(final CounterPreferences counterPreferences) { 8 | this.counterPreferences = counterPreferences; 9 | } 10 | 11 | public boolean shouldShowMessage() { 12 | return counterPreferences.getCounter() >= 42; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/code-health-check.yml: -------------------------------------------------------------------------------- 1 | name: Code health check 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | tests: 11 | name: Run tests and analyze code coverage 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out code 15 | uses: actions/checkout@v2 16 | 17 | - name: Set up JDK 11 18 | uses: actions/setup-java@v2 19 | with: 20 | distribution: adopt 21 | java-version: 11 22 | 23 | - name: Run unit tests with coverage 24 | run: ./gradlew jacocoTestReportDebug 25 | 26 | - name: Upload coverage to Codecov 27 | uses: codecov/codecov-action@v2 28 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/main/java/com/github/ivanshafran/sharedpreferencesmock/internal/SharedPreferencesFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.internal; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | public class SharedPreferencesFactory { 6 | 7 | private boolean isThreadSafe; 8 | 9 | public SharedPreferencesFactory(final boolean isThreadSafe) { 10 | this.isThreadSafe = isThreadSafe; 11 | } 12 | 13 | public SharedPreferences create() { 14 | if (isThreadSafe) { 15 | return new ThreadSafeSharedPreferencesMock(); 16 | } else { 17 | return new SharedPreferencesMock(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /sharedpreferencesmock/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /sample/src/main/java/com/github/ivanshafran/sharedpreferencesmock/sample/CounterPreferences.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.sample; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import androidx.annotation.NonNull; 6 | 7 | public class CounterPreferences { 8 | 9 | private static final String FILENAME = "filename"; 10 | private static final String KEY = "key"; 11 | 12 | private final SharedPreferences sharedPreferences; 13 | 14 | public CounterPreferences(@NonNull final Context context) { 15 | sharedPreferences = context.getSharedPreferences(FILENAME, Context.MODE_PRIVATE); 16 | } 17 | 18 | public int getCounter() { 19 | return sharedPreferences.getInt(KEY, 0); 20 | } 21 | 22 | public void setCounter(final int counter) { 23 | sharedPreferences.edit().putInt(KEY, counter).apply(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | spek_version=2.0.5 16 | android_junit5_version=1.6.2.0 17 | junit5_runner=0.2.2 18 | jacoco_version=0.8.1 19 | kotlin_version = 1.3.72 20 | android.useAndroidX=true 21 | android.enableJetifier=false 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ivan Shafran 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 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | defaultConfig { 6 | applicationId "com.github.ivanshafran.sharedpreferencesmock.sample" 7 | minSdkVersion 15 8 | targetSdkVersion 29 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | implementation 'androidx.appcompat:appcompat:1.2.0' 29 | 30 | testImplementation project(':sharedpreferencesmock') 31 | testImplementation 'junit:junit:4.13' 32 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 34 | } 35 | -------------------------------------------------------------------------------- /sample/src/test/java/com/github/ivanshafran/sharedpreferencesmock/sample/ShowMessageLogicTest.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.sample; 2 | 3 | import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class ShowMessageLogicTest { 10 | 11 | private final SPMockBuilder spMockBuilder = new SPMockBuilder(); 12 | private CounterPreferences counterPreferences; 13 | private ShowMessageLogic showMessageLogic; 14 | 15 | @Before 16 | public void setUp() { 17 | counterPreferences = new CounterPreferences(spMockBuilder.createContext()); 18 | showMessageLogic = new ShowMessageLogic(counterPreferences); 19 | } 20 | 21 | @Test 22 | public void on42CounterItShouldShowMessage() { 23 | counterPreferences.setCounter(42); 24 | 25 | Assert.assertTrue(showMessageLogic.shouldShowMessage()); 26 | } 27 | 28 | @Test 29 | public void onLessThan42ItShouldNotShowMessage() { 30 | counterPreferences.setCounter(41); 31 | 32 | Assert.assertFalse(showMessageLogic.shouldShowMessage()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/internal/SharedPreferencesFactoryTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.internal 2 | 3 | import android.content.SharedPreferences 4 | import org.spekframework.spek2.Spek 5 | import org.spekframework.spek2.style.specification.describe 6 | import kotlin.test.assertTrue 7 | 8 | class SharedPreferencesFactoryTest : Spek({ 9 | 10 | describe("shared preferences factory") { 11 | lateinit var preferences: SharedPreferences 12 | 13 | context("thread safe flag is enabled") { 14 | beforeEachTest { 15 | preferences = SharedPreferencesFactory(true).create() 16 | } 17 | 18 | it("should create thread safe preferences instance") { 19 | assertTrue { preferences is ThreadSafeSharedPreferencesMock } 20 | } 21 | } 22 | 23 | context("thread safe flag is disabled") { 24 | beforeEachTest { 25 | preferences = SharedPreferencesFactory(false).create() 26 | } 27 | 28 | it("should create not thread safe preferences instance") { 29 | assertTrue { preferences is SharedPreferencesMock } 30 | } 31 | } 32 | } 33 | 34 | }) 35 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/SPMockBuilderTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock 2 | 3 | import com.github.ivanshafran.sharedpreferencesmock.internal.ContextMock 4 | import com.github.ivanshafran.sharedpreferencesmock.internal.SPContextWrapperMock 5 | import org.spekframework.spek2.Spek 6 | import org.spekframework.spek2.style.specification.describe 7 | import kotlin.test.assertNotNull 8 | import kotlin.test.assertTrue 9 | 10 | class SPMockBuilderTest : Spek({ 11 | describe("SPMockBuilder") { 12 | val mockBuilder by memoized { SPMockBuilder() } 13 | 14 | context("on createSharedPreferences") { 15 | it("should return not null shared preference mock instance") { 16 | assertNotNull(mockBuilder.createSharedPreferences()) 17 | } 18 | } 19 | 20 | context("on createContext") { 21 | it("should return context wrapper mock instance") { 22 | assertTrue { mockBuilder.createContext() is SPContextWrapperMock } 23 | } 24 | } 25 | 26 | context("on wrapContext") { 27 | it("should return context wrapper mock instance") { 28 | assertTrue { mockBuilder.wrapContext(ContextMock()) is SPContextWrapperMock } 29 | } 30 | } 31 | } 32 | }) 33 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/main/java/com/github/ivanshafran/sharedpreferencesmock/SPMockBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import androidx.annotation.NonNull; 6 | 7 | import com.github.ivanshafran.sharedpreferencesmock.internal.ContextMock; 8 | import com.github.ivanshafran.sharedpreferencesmock.internal.SPContextWrapperMock; 9 | import com.github.ivanshafran.sharedpreferencesmock.internal.SharedPreferencesFactory; 10 | 11 | public final class SPMockBuilder { 12 | 13 | private boolean isThreadSafe = false; 14 | 15 | public SPMockBuilder() { 16 | 17 | } 18 | 19 | public SPMockBuilder setThreadSafe(final boolean isThreadSafe) { 20 | this.isThreadSafe = isThreadSafe; 21 | return this; 22 | } 23 | 24 | public SharedPreferences createSharedPreferences() { 25 | return createFactory().create(); 26 | } 27 | 28 | public Context createContext() { 29 | return new SPContextWrapperMock(new ContextMock(), createFactory()); 30 | } 31 | 32 | public Context wrapContext(@NonNull final Context context) { 33 | return new SPContextWrapperMock(context, createFactory()); 34 | } 35 | 36 | private SharedPreferencesFactory createFactory() { 37 | return new SharedPreferencesFactory(isThreadSafe); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/main/java/com/github/ivanshafran/sharedpreferencesmock/internal/SPContextWrapperMock.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.internal; 2 | 3 | import android.content.Context; 4 | import android.content.ContextWrapper; 5 | import android.content.SharedPreferences; 6 | import androidx.annotation.NonNull; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class SPContextWrapperMock extends ContextWrapper { 12 | 13 | private final Map preferencesMap = new HashMap<>(); 14 | private final SharedPreferencesFactory factory; 15 | 16 | public SPContextWrapperMock(@NonNull final Context base, @NonNull final SharedPreferencesFactory factory) { 17 | super(base); 18 | this.factory = factory; 19 | } 20 | 21 | @Override 22 | public SharedPreferences getSharedPreferences(final String name, final int mode) { 23 | if (preferencesMap.containsKey(name)) { 24 | return preferencesMap.get(name); 25 | } else { 26 | final SharedPreferences sharedPreferences = factory.create(); 27 | preferencesMap.put(name, sharedPreferences); 28 | return sharedPreferences; 29 | } 30 | } 31 | 32 | @Override 33 | public boolean deleteSharedPreferences(final String name) { 34 | preferencesMap.remove(name); 35 | return true; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | # We'll run this workflow when a new GitHub release is created 6 | types: [released] 7 | 8 | jobs: 9 | publish: 10 | name: Release build and publish 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out code 14 | uses: actions/checkout@v2 15 | - name: Set up JDK 11 16 | uses: actions/setup-java@v2 17 | with: 18 | distribution: adopt 19 | java-version: 11 20 | 21 | # Builds the release artifacts of the library 22 | - name: Release build 23 | run: ./gradlew :sharedpreferencesmock:assembleRelease 24 | 25 | # Generates other artifacts (javadocJar is optional) 26 | - name: Source jar 27 | run: ./gradlew androidSourcesJar 28 | 29 | # Runs upload, and then closes & releases the repository 30 | - name: Publish to MavenCentral 31 | run: ./gradlew publishReleasePublicationToSonatypeRepository --max-workers 1 closeAndReleaseSonatypeStagingRepository 32 | env: 33 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} 34 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 35 | SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} 36 | SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} 37 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }} 38 | SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }} 39 | -------------------------------------------------------------------------------- /scripts/publish-root.gradle: -------------------------------------------------------------------------------- 1 | // Create variables with empty default values 2 | ext["signing.keyId"] = '' 3 | ext["signing.password"] = '' 4 | ext["signing.key"] = '' 5 | ext["ossrhUsername"] = '' 6 | ext["ossrhPassword"] = '' 7 | ext["sonatypeStagingProfileId"] = '' 8 | 9 | File secretPropsFile = project.rootProject.file('local.properties') 10 | if (secretPropsFile.exists()) { 11 | // Read local.properties file first if it exists 12 | Properties p = new Properties() 13 | new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) } 14 | p.each { name, value -> ext[name] = value } 15 | } else { 16 | // Use system environment variables 17 | ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') 18 | ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') 19 | ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') 20 | ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') 21 | ext["signing.password"] = System.getenv('SIGNING_PASSWORD') 22 | ext["signing.key"] = System.getenv('SIGNING_KEY') 23 | } 24 | 25 | // Set up Sonatype repository 26 | nexusPublishing { 27 | repositories { 28 | sonatype { 29 | stagingProfileId = sonatypeStagingProfileId 30 | username = ossrhUsername 31 | password = ossrhPassword 32 | nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) 33 | snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/SharedPreferencesDeleteTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock 2 | 3 | import org.spekframework.spek2.Spek 4 | import org.spekframework.spek2.style.specification.describe 5 | import kotlin.test.assertFalse 6 | import kotlin.test.assertTrue 7 | 8 | class SharedPreferencesDeleteTest : Spek({ 9 | 10 | for (isThreadSafe in listOf(false, true)) { 11 | describe("deleting elements from shared preferences mock with thread safety: $isThreadSafe") { 12 | val sharedPreferences by memoized { 13 | SPMockBuilder().setThreadSafe(isThreadSafe).createSharedPreferences() 14 | } 15 | 16 | context("on calling clear in edit builder") { 17 | beforeEachTest { 18 | sharedPreferences.edit().putInt("1", 1).putBoolean("2", false).commit() 19 | sharedPreferences.edit().clear().commit() 20 | } 21 | 22 | it("should delete all items") { 23 | assertTrue(sharedPreferences.all.isEmpty()) 24 | } 25 | } 26 | 27 | context("on calling remove in edit builder") { 28 | beforeEachTest { 29 | sharedPreferences.edit().putInt("1", 1).putBoolean("2", false).commit() 30 | sharedPreferences.edit().remove("1").commit() 31 | } 32 | 33 | it("should remove only specific item") { 34 | assertFalse(sharedPreferences.contains("1")) 35 | assertTrue(sharedPreferences.contains("2")) 36 | } 37 | } 38 | } 39 | } 40 | 41 | }) 42 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/SharedPreferencesStringSetTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock 2 | 3 | import org.spekframework.spek2.Spek 4 | import org.spekframework.spek2.style.specification.describe 5 | import kotlin.test.assertEquals 6 | import kotlin.test.assertNull 7 | import kotlin.test.assertTrue 8 | 9 | class SharedPreferencesStringSetTest : Spek({ 10 | 11 | val key = "key" 12 | for (isThreadSafe in listOf(false, true)) { 13 | describe("string set processing in shared preferences with thread safety: $isThreadSafe") { 14 | val sharedPreferences by memoized { 15 | SPMockBuilder().setThreadSafe(isThreadSafe).createSharedPreferences() 16 | } 17 | 18 | context("on null string set") { 19 | beforeEachTest { 20 | sharedPreferences.edit().putStringSet(key, null).commit() 21 | } 22 | 23 | it("should save and return null string set") { 24 | assertNull(sharedPreferences.getStringSet(key, null)) 25 | } 26 | } 27 | 28 | context("on nonnull string set") { 29 | val stringSet = setOf("1", "2") 30 | var returnedSet: Set? = null 31 | beforeEachTest { 32 | sharedPreferences.edit().putStringSet(key, stringSet).commit() 33 | returnedSet = sharedPreferences.getStringSet(key, null) 34 | } 35 | 36 | it("should save and return copy of it") { 37 | assertEquals(stringSet, returnedSet) 38 | assertTrue { returnedSet !== stringSet } 39 | } 40 | } 41 | } 42 | } 43 | 44 | }) 45 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/SharedPreferencesDefaultValuesTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock 2 | 3 | import org.spekframework.spek2.Spek 4 | import org.spekframework.spek2.style.specification.describe 5 | import kotlin.test.assertEquals 6 | 7 | class SharedPreferencesDefaultValuesTest : Spek({ 8 | 9 | for (isThreadSafe in listOf(false, true)) { 10 | describe("shared preferences mock with thread safety: $isThreadSafe") { 11 | context("no elements set") { 12 | 13 | val mock by memoized { SPMockBuilder().setThreadSafe(isThreadSafe).createSharedPreferences() } 14 | 15 | it("should return default value for getString") { 16 | val defaultString = "default string" 17 | assertEquals(defaultString, mock.getString(null, defaultString)) 18 | } 19 | 20 | it("should return default value for getStringSet") { 21 | val defaultStringSet = setOf("Some value") 22 | assertEquals(defaultStringSet, mock.getStringSet(null, defaultStringSet)) 23 | } 24 | 25 | it("should return default value for getInt") { 26 | val defaultInt = 42 27 | assertEquals(defaultInt, mock.getInt(null, defaultInt)) 28 | } 29 | 30 | it("should return default value for getLong") { 31 | val defaultLong = 42L 32 | assertEquals(defaultLong, mock.getLong(null, defaultLong)) 33 | } 34 | 35 | it("should return default value for getFloat") { 36 | val defaultFloat = 42f 37 | assertEquals(defaultFloat, mock.getFloat(null, defaultFloat)) 38 | } 39 | 40 | it("should return default value for getBoolean") { 41 | val defaultBoolean = true 42 | assertEquals(defaultBoolean, mock.getBoolean(null, defaultBoolean)) 43 | } 44 | 45 | it("should return default value for getBoolean") { 46 | val defaultBoolean = true 47 | assertEquals(defaultBoolean, mock.getBoolean(null, defaultBoolean)) 48 | } 49 | } 50 | } 51 | } 52 | 53 | }) 54 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/SPContextWrapperMockSharedPreferencesTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock 2 | 3 | import android.content.SharedPreferences 4 | import com.github.ivanshafran.sharedpreferencesmock.internal.ContextMock 5 | import com.github.ivanshafran.sharedpreferencesmock.internal.SPContextWrapperMock 6 | import com.github.ivanshafran.sharedpreferencesmock.internal.SharedPreferencesFactory 7 | import org.spekframework.spek2.Spek 8 | import org.spekframework.spek2.style.specification.describe 9 | import kotlin.test.assertNotNull 10 | import kotlin.test.assertTrue 11 | 12 | class SPContextWrapperMockSharedPreferencesTest : Spek({ 13 | 14 | describe("context mock") { 15 | val context by memoized { 16 | SPContextWrapperMock( 17 | ContextMock(), 18 | SharedPreferencesFactory(false) 19 | ) 20 | } 21 | 22 | context("on first preferences request") { 23 | 24 | it("should return non null value of SharedPreferences") { 25 | assertNotNull(context.getSharedPreferences("", 0)) 26 | } 27 | } 28 | 29 | context("on several request with the same name") { 30 | 31 | it("should return the same instance") { 32 | assertTrue { context.getSharedPreferences("", 0) === context.getSharedPreferences("", 0) } 33 | } 34 | } 35 | 36 | context("on several request with different names") { 37 | 38 | it("should return different instances") { 39 | assertTrue { context.getSharedPreferences("1", 0) !== context.getSharedPreferences("2", 0) } 40 | } 41 | } 42 | 43 | context("after preferences is deleted") { 44 | 45 | context("on the same preference request") { 46 | lateinit var first: SharedPreferences 47 | lateinit var second: SharedPreferences 48 | 49 | beforeEachTest { 50 | first = context.getSharedPreferences("", 0) 51 | context.deleteSharedPreferences("") 52 | second = context.getSharedPreferences("", 0) 53 | } 54 | 55 | it("should return new instance") { 56 | assertTrue { first !== second } 57 | } 58 | } 59 | } 60 | } 61 | 62 | }) 63 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /sharedpreferencesmock/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'jacoco' 4 | apply plugin: 'de.mannodermaus.android-junit5' 5 | 6 | ext { 7 | PUBLISH_GROUP_ID = 'io.github.ivanshafran' 8 | PUBLISH_VERSION = '1.2.4' 9 | PUBLISH_ARTIFACT_ID = 'shared-preferences-mock' 10 | } 11 | 12 | apply from: "${rootProject.projectDir}/scripts/publish-module.gradle" 13 | 14 | android { 15 | compileSdkVersion 28 16 | 17 | defaultConfig { 18 | testInstrumentationRunnerArguments 19 | minSdkVersion 15 20 | targetSdkVersion 28 21 | versionCode 1 22 | versionName "1.0" 23 | 24 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 25 | testInstrumentationRunnerArgument "runnerBuilder", "de.mannodermaus.junit5.AndroidJUnit5Builder" 26 | 27 | } 28 | 29 | buildTypes { 30 | release { 31 | minifyEnabled false 32 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | 36 | testOptions { 37 | junitPlatform { 38 | filters { 39 | engines { 40 | include 'spek2' 41 | } 42 | } 43 | jacocoOptions { 44 | // here goes all jacoco config, for example 45 | html.enabled = true 46 | xml.enabled = true 47 | csv.enabled = true 48 | unitTests.all { 49 | testLogging.events = ["passed", "skipped", "failed"] 50 | } 51 | } 52 | } 53 | } 54 | 55 | compileOptions { 56 | sourceCompatibility JavaVersion.VERSION_1_8 57 | targetCompatibility JavaVersion.VERSION_1_8 58 | } 59 | 60 | packagingOptions { 61 | exclude 'META-INF/LICENSE.md' 62 | exclude 'META-INF/LICENSE-notice.md' 63 | } 64 | } 65 | 66 | dependencies { 67 | implementation fileTree(dir: 'libs', include: ['*.jar']) 68 | 69 | implementation 'androidx.appcompat:appcompat:1.2.0' 70 | 71 | // spek2 72 | testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 73 | testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" 74 | testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" 75 | 76 | testImplementation "org.spekframework.spek2:spek-dsl-jvm:$spek_version" 77 | testImplementation "org.spekframework.spek2:spek-runner-junit5:$spek_version" 78 | 79 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 80 | androidTestImplementation "de.mannodermaus.junit5:android-test-core:1.2.0" 81 | androidTestRuntimeOnly "de.mannodermaus.junit5:android-test-runner:1.2.0" 82 | } 83 | -------------------------------------------------------------------------------- /scripts/publish-module.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven-publish' 2 | apply plugin: 'signing' 3 | 4 | task androidSourcesJar(type: Jar) { 5 | archiveClassifier.set('sources') 6 | if (project.plugins.findPlugin("com.android.library")) { 7 | // For Android libraries 8 | from android.sourceSets.main.java.srcDirs 9 | from android.sourceSets.main.kotlin.srcDirs 10 | } else { 11 | // For pure Kotlin libraries, in case you have them 12 | from sourceSets.main.java.srcDirs 13 | from sourceSets.main.kotlin.srcDirs 14 | } 15 | } 16 | 17 | artifacts { 18 | archives androidSourcesJar 19 | } 20 | 21 | group = PUBLISH_GROUP_ID 22 | version = PUBLISH_VERSION 23 | 24 | afterEvaluate { 25 | publishing { 26 | publications { 27 | release(MavenPublication) { 28 | // The coordinates of the library, being set from variables that 29 | // we'll set up later 30 | groupId PUBLISH_GROUP_ID 31 | artifactId PUBLISH_ARTIFACT_ID 32 | version PUBLISH_VERSION 33 | 34 | // Two artifacts, the `aar` (or `jar`) and the sources 35 | if (project.plugins.findPlugin("com.android.library")) { 36 | from components.release 37 | } else { 38 | from components.java 39 | } 40 | 41 | artifact androidSourcesJar 42 | 43 | // Mostly self-explanatory metadata 44 | pom { 45 | name = PUBLISH_ARTIFACT_ID 46 | description = 'Shared preferences mock library' 47 | url = 'https://github.com/IvanShafran/shared-preferences-mock' 48 | licenses { 49 | license { 50 | name = 'MIT License' 51 | url = 'https://github.com/IvanShafran/shared-preferences-mock/blob/main/LICENSE' 52 | } 53 | } 54 | developers { 55 | developer { 56 | id = 'IvanShafran' 57 | name = 'Ivan Shafran' 58 | email = 'shafran.fun@gmail.com' 59 | } 60 | } 61 | 62 | // Version control info - if you're using GitHub, follow the 63 | // format as seen here 64 | scm { 65 | connection = 'scm:git:github.com/IvanShafran/shared-preferences-mock.git' 66 | developerConnection = 'scm:git:ssh://github.com/IvanShafran/shared-preferences-mock.git' 67 | url = 'https://github.com/IvanShafran/shared-preferences-mock/tree/main' 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | signing { 76 | useInMemoryPgpKeys( 77 | rootProject.ext["signing.keyId"], 78 | rootProject.ext["signing.key"], 79 | rootProject.ext["signing.password"], 80 | ) 81 | sign publishing.publications 82 | } 83 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/SharedPreferencesPutGetTests.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock 2 | 3 | import org.spekframework.spek2.Spek 4 | import org.spekframework.spek2.lifecycle.CachingMode 5 | import org.spekframework.spek2.style.specification.describe 6 | import kotlin.test.assertEquals 7 | import kotlin.test.assertTrue 8 | 9 | class SharedPreferencesPutGetTests : Spek({ 10 | 11 | val stringKey = "string key" 12 | val stringValue = "string value" 13 | 14 | val stringSetKey = "string set key" 15 | val stringSetValue = setOf("first", "second", "third") 16 | 17 | val intKey = "int key" 18 | val intValue = 42 19 | 20 | val longKey = "long key" 21 | val longValue = 42L 22 | 23 | val floatKey = "float key" 24 | val floatValue = 42f 25 | 26 | val booleanKey = "boolean key" 27 | val booleanValue = true 28 | 29 | for (isThreadSafe in listOf(false, true)) { 30 | describe("shared preferences mock with thread safety: $isThreadSafe") { 31 | 32 | val sharedPreferences by memoized(CachingMode.SCOPE) { 33 | SPMockBuilder().setThreadSafe(isThreadSafe).createSharedPreferences() 34 | } 35 | 36 | context("put values for each type via edit and commit") { 37 | beforeGroup { 38 | sharedPreferences 39 | .edit() 40 | .putString(stringKey, stringValue) 41 | .putStringSet(stringSetKey, stringSetValue) 42 | .putInt(intKey, intValue) 43 | .putLong(longKey, longValue) 44 | .putFloat(floatKey, floatValue) 45 | .putBoolean(booleanKey, booleanValue) 46 | .commit() 47 | } 48 | 49 | it("should return correct string by key") { 50 | assertEquals(stringValue, sharedPreferences.getString(stringKey, "default")) 51 | } 52 | 53 | it("should return correct string set by key") { 54 | assertEquals(stringSetValue, sharedPreferences.getStringSet(stringSetKey, setOf())) 55 | } 56 | 57 | it("should return correct int by key") { 58 | assertEquals(intValue, sharedPreferences.getInt(intKey, 0)) 59 | } 60 | 61 | it("should return correct long by key") { 62 | assertEquals(longValue, sharedPreferences.getLong(longKey, 0L)) 63 | } 64 | 65 | it("should return correct float by key") { 66 | assertEquals(floatValue, sharedPreferences.getFloat(floatKey, 0f)) 67 | } 68 | 69 | it("should return correct boolean by key") { 70 | assertEquals(booleanValue, sharedPreferences.getBoolean(booleanKey, true)) 71 | } 72 | 73 | it("should return map with all values") { 74 | assertTrue { sharedPreferences.all.size == 6 } 75 | } 76 | 77 | it("should contains put key") { 78 | assertTrue { sharedPreferences.contains(stringKey) } 79 | } 80 | } 81 | } 82 | } 83 | 84 | }) 85 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/ContextMockThrowsTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock 2 | 3 | import android.content.Context 4 | import android.content.res.TypedArray 5 | import android.util.AttributeSet 6 | import com.github.ivanshafran.sharedpreferencesmock.internal.ContextMock 7 | import org.spekframework.spek2.Spek 8 | import org.spekframework.spek2.style.specification.describe 9 | import java.lang.reflect.InvocationTargetException 10 | import kotlin.reflect.KFunction 11 | import kotlin.reflect.KVisibility 12 | import kotlin.reflect.full.createType 13 | import kotlin.reflect.full.functions 14 | import kotlin.test.assertFailsWith 15 | 16 | private val INT_TYPE = Int::class.createType() 17 | private val ANY_FUNCTIONS = listOf( 18 | Context::equals, 19 | Context::hashCode, 20 | Context::toString 21 | ) 22 | 23 | // For overload functions 24 | private fun getFunctionReference(reference: Type): Type { 25 | return reference 26 | } 27 | 28 | private val CONTEXT_FINAL_FUNCTIONS = listOf( 29 | Context::getColorStateList, 30 | Context::getDrawable, 31 | getFunctionReference String>(Context::getString), 32 | getFunctionReference) -> String>(Context::getString), 33 | Context::getText, 34 | Context::getColor, 35 | getFunctionReference TypedArray>(Context::obtainStyledAttributes), 36 | getFunctionReference TypedArray>(Context::obtainStyledAttributes), 37 | getFunctionReference TypedArray>(Context::obtainStyledAttributes), 38 | getFunctionReference TypedArray>(Context::obtainStyledAttributes), 39 | getFunctionReference) -> Any>(Context::getSystemService) 40 | ) 41 | 42 | class SPMockContextThrowsTest : Spek({ 43 | 44 | val contextMock = ContextMock() 45 | val functions = Context::class.functions 46 | .filter { it.visibility == KVisibility.PUBLIC } 47 | .filter { it !in ANY_FUNCTIONS + CONTEXT_FINAL_FUNCTIONS } 48 | 49 | fun getCallArgumentMocks(function: KFunction<*>): Array { 50 | return function.parameters 51 | .subList(1, function.parameters.size) 52 | .map { 53 | when (it.type) { 54 | INT_TYPE -> 0 55 | else -> null 56 | } 57 | } 58 | .toTypedArray() 59 | } 60 | 61 | fun rethrowInvocationTargetExceptionCause(block: () -> Unit) { 62 | try { 63 | block() 64 | } catch (e: InvocationTargetException) { 65 | throw e.cause ?: return 66 | } 67 | } 68 | 69 | describe("context mock") { 70 | 71 | functions.forEach { function -> 72 | context("on $function call") { 73 | it("should throw UnsupportedOperationException") { 74 | assertFailsWith { 75 | rethrowInvocationTargetExceptionCause { 76 | val args = getCallArgumentMocks(function) 77 | function.call(contextMock, *args) 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | }) 86 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/main/java/com/github/ivanshafran/sharedpreferencesmock/internal/ThreadSafeSharedPreferencesMock.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.internal; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | final class ThreadSafeSharedPreferencesMock extends SharedPreferencesMock { 9 | 10 | private final Object lock = new Object(); 11 | 12 | @Override 13 | public Map getAll() { 14 | synchronized (lock) { 15 | return super.getAll(); 16 | } 17 | } 18 | 19 | @Nullable 20 | @Override 21 | public String getString(final String key, @Nullable final String defValue) { 22 | synchronized (lock) { 23 | return super.getString(key, defValue); 24 | } 25 | } 26 | 27 | @Nullable 28 | @Override 29 | public Set getStringSet(final String key, @Nullable final Set defValues) { 30 | synchronized (lock) { 31 | return super.getStringSet(key, defValues); 32 | } 33 | } 34 | 35 | @Override 36 | public int getInt(final String key, final int defValue) { 37 | synchronized (lock) { 38 | return super.getInt(key, defValue); 39 | } 40 | } 41 | 42 | @Override 43 | public long getLong(final String key, final long defValue) { 44 | synchronized (lock) { 45 | return super.getLong(key, defValue); 46 | } 47 | } 48 | 49 | @Override 50 | public float getFloat(final String key, final float defValue) { 51 | synchronized (lock) { 52 | return super.getFloat(key, defValue); 53 | } 54 | } 55 | 56 | @Override 57 | public boolean getBoolean(final String key, final boolean defValue) { 58 | synchronized (lock) { 59 | return super.getBoolean(key, defValue); 60 | } 61 | } 62 | 63 | @Override 64 | public boolean contains(final String key) { 65 | synchronized (lock) { 66 | return super.contains(key); 67 | } 68 | } 69 | 70 | @Override 71 | public Editor edit() { 72 | return new ThreadSafeEditor(); 73 | } 74 | 75 | @Override 76 | public void registerOnSharedPreferenceChangeListener(final OnSharedPreferenceChangeListener listener) { 77 | synchronized (lock) { 78 | super.registerOnSharedPreferenceChangeListener(listener); 79 | } 80 | } 81 | 82 | @Override 83 | public void unregisterOnSharedPreferenceChangeListener(final OnSharedPreferenceChangeListener listener) { 84 | synchronized (lock) { 85 | super.unregisterOnSharedPreferenceChangeListener(listener); 86 | } 87 | } 88 | 89 | public class ThreadSafeEditor extends SharedPreferencesMock.EditorImpl { 90 | 91 | private final Object editorLock = new Object(); 92 | 93 | @Override 94 | public Editor putString(final String key, @Nullable final String value) { 95 | synchronized (editorLock) { 96 | super.putString(key, value); 97 | return this; 98 | } 99 | } 100 | 101 | @Override 102 | public Editor putStringSet(final String key, @Nullable final Set values) { 103 | synchronized (editorLock) { 104 | super.putStringSet(key, values); 105 | return this; 106 | } 107 | } 108 | 109 | @Override 110 | public Editor putInt(final String key, final int value) { 111 | synchronized (editorLock) { 112 | super.putInt(key, value); 113 | return this; 114 | } 115 | } 116 | 117 | @Override 118 | public Editor putLong(final String key, final long value) { 119 | synchronized (editorLock) { 120 | super.putLong(key, value); 121 | return this; 122 | } 123 | } 124 | 125 | @Override 126 | public Editor putFloat(final String key, final float value) { 127 | synchronized (editorLock) { 128 | super.putFloat(key, value); 129 | return this; 130 | } 131 | } 132 | 133 | @Override 134 | public Editor putBoolean(final String key, final boolean value) { 135 | synchronized (editorLock) { 136 | super.putBoolean(key, value); 137 | return this; 138 | } 139 | } 140 | 141 | @Override 142 | public Editor remove(final String key) { 143 | synchronized (editorLock) { 144 | super.remove(key); 145 | return this; 146 | } 147 | } 148 | 149 | @Override 150 | public Editor clear() { 151 | synchronized (editorLock) { 152 | super.clear(); 153 | return this; 154 | } 155 | } 156 | 157 | @Override 158 | public boolean commit() { 159 | synchronized (editorLock) { 160 | synchronized (lock) { 161 | return super.commit(); 162 | } 163 | } 164 | } 165 | 166 | @Override 167 | public void apply() { 168 | synchronized (editorLock) { 169 | synchronized (lock) { 170 | super.apply(); 171 | } 172 | } 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/test/java/com/github/ivanshafran/sharedpreferencesmock/SharedPreferencesListenersTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock 2 | 3 | import android.content.SharedPreferences 4 | import org.spekframework.spek2.Spek 5 | import org.spekframework.spek2.style.specification.describe 6 | import kotlin.test.assertEquals 7 | import kotlin.test.assertFalse 8 | import kotlin.test.assertTrue 9 | 10 | class SharedPreferencesListenersTest : Spek({ 11 | 12 | class ListenerMock : SharedPreferences.OnSharedPreferenceChangeListener { 13 | private var eventSharedPreferences: SharedPreferences? = null 14 | private var eventKey: String? = null 15 | private var isCalled = false 16 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { 17 | eventSharedPreferences = sharedPreferences 18 | eventKey = key 19 | isCalled = true 20 | } 21 | 22 | fun assertSharedPreferences(instance: SharedPreferences?) { 23 | assertTrue { eventSharedPreferences === instance } 24 | } 25 | 26 | fun assertKey(key: String?) { 27 | assertEquals(eventKey, key) 28 | } 29 | 30 | fun assertNotCalled() { 31 | assertFalse(isCalled) 32 | } 33 | } 34 | 35 | val stringKey = "key" 36 | 37 | for (isThreadSafe in listOf(false, true)) { 38 | describe("shared preferences listeners with thread safety: $isThreadSafe") { 39 | val sharedPreferences by memoized { 40 | SPMockBuilder().setThreadSafe(isThreadSafe).createSharedPreferences() 41 | } 42 | val listener by memoized { ListenerMock() } 43 | 44 | context("register listener and put string") { 45 | beforeEachTest { 46 | sharedPreferences.registerOnSharedPreferenceChangeListener(listener) 47 | sharedPreferences.edit().putString(stringKey, "").apply() 48 | } 49 | 50 | it("should call listener with string key and shared preferences instance") { 51 | listener.assertKey(stringKey) 52 | listener.assertSharedPreferences(sharedPreferences) 53 | } 54 | } 55 | 56 | context("register listener and remove string") { 57 | beforeEachTest { 58 | sharedPreferences.edit().putString(stringKey, "").apply() 59 | sharedPreferences.registerOnSharedPreferenceChangeListener(listener) 60 | sharedPreferences.edit().remove(stringKey).apply() 61 | } 62 | 63 | it("should call listener with string key and shared preferences instance") { 64 | listener.assertKey(stringKey) 65 | listener.assertSharedPreferences(sharedPreferences) 66 | } 67 | } 68 | 69 | context("register listener and clear all") { 70 | beforeEachTest { 71 | sharedPreferences.edit().putString(stringKey, "").apply() 72 | sharedPreferences.registerOnSharedPreferenceChangeListener(listener) 73 | sharedPreferences.edit().clear().apply() 74 | } 75 | 76 | it("should not call listener") { 77 | // ?: see original implementation 78 | // https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/SharedPreferencesImpl.java#498 79 | listener.assertNotCalled() 80 | } 81 | } 82 | 83 | context("register and then unregister listener and put string") { 84 | beforeEachTest { 85 | sharedPreferences.registerOnSharedPreferenceChangeListener(listener) 86 | sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) 87 | sharedPreferences.edit().putString(stringKey, "").apply() 88 | } 89 | 90 | it("should not call listener") { 91 | listener.assertNotCalled() 92 | } 93 | } 94 | 95 | context("register and remove nonexistent item") { 96 | beforeEachTest { 97 | sharedPreferences.registerOnSharedPreferenceChangeListener(listener) 98 | sharedPreferences.edit().remove(stringKey).apply() 99 | } 100 | 101 | it("should not call listener") { 102 | listener.assertNotCalled() 103 | } 104 | } 105 | 106 | context("register and put the same existing item") { 107 | beforeEachTest { 108 | sharedPreferences.edit().putString(stringKey, "").apply() 109 | sharedPreferences.registerOnSharedPreferenceChangeListener(listener) 110 | sharedPreferences.edit().putString(stringKey, "").apply() 111 | } 112 | 113 | it("should not call listener") { 114 | listener.assertNotCalled() 115 | } 116 | } 117 | 118 | context("register and put new value to the existing item") { 119 | beforeEachTest { 120 | sharedPreferences.edit().putString(stringKey, "old").apply() 121 | sharedPreferences.registerOnSharedPreferenceChangeListener(listener) 122 | sharedPreferences.edit().putString(stringKey, "new").apply() 123 | } 124 | 125 | it("should call listener with string key and shared preferences instance") { 126 | listener.assertKey(stringKey) 127 | listener.assertSharedPreferences(sharedPreferences) 128 | } 129 | } 130 | } 131 | } 132 | }) 133 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 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 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/main/java/com/github/ivanshafran/sharedpreferencesmock/internal/SharedPreferencesMock.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.internal; 2 | 3 | import android.content.SharedPreferences; 4 | import androidx.annotation.NonNull; 5 | import androidx.annotation.Nullable; 6 | 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | class SharedPreferencesMock implements SharedPreferences { 15 | 16 | private final Map preferencesMap = new HashMap<>(); 17 | private final Set listeners = new HashSet<>(); 18 | 19 | @Override 20 | public Map getAll() { 21 | return new HashMap<>(preferencesMap); 22 | } 23 | 24 | @Nullable 25 | @Override 26 | public String getString(final String key, @Nullable final String defValue) { 27 | final String string = (String) preferencesMap.get(key); 28 | return string != null ? string : defValue; 29 | } 30 | 31 | @Nullable 32 | @Override 33 | public Set getStringSet(final String key, @Nullable final Set defValues) { 34 | //noinspection unchecked 35 | final Set stringSet = (Set) preferencesMap.get(key); 36 | return stringSet != null ? stringSet : defValues; 37 | } 38 | 39 | @Override 40 | public int getInt(final String key, final int defValue) { 41 | final Integer integer = (Integer) preferencesMap.get(key); 42 | return integer != null ? integer : defValue; 43 | } 44 | 45 | @Override 46 | public long getLong(final String key, final long defValue) { 47 | final Long longValue = (Long) preferencesMap.get(key); 48 | return longValue != null ? longValue : defValue; 49 | } 50 | 51 | @Override 52 | public float getFloat(final String key, final float defValue) { 53 | final Float floatValue = (Float) preferencesMap.get(key); 54 | return floatValue != null ? floatValue : defValue; 55 | } 56 | 57 | @Override 58 | public boolean getBoolean(final String key, final boolean defValue) { 59 | Boolean booleanValue = (Boolean) preferencesMap.get(key); 60 | return booleanValue != null ? booleanValue : defValue; 61 | } 62 | 63 | @Override 64 | public boolean contains(final String key) { 65 | return preferencesMap.containsKey(key); 66 | } 67 | 68 | @Override 69 | public Editor edit() { 70 | return new EditorImpl(); 71 | } 72 | 73 | @Override 74 | public void registerOnSharedPreferenceChangeListener(final OnSharedPreferenceChangeListener listener) { 75 | listeners.add(listener); 76 | } 77 | 78 | @Override 79 | public void unregisterOnSharedPreferenceChangeListener(final OnSharedPreferenceChangeListener listener) { 80 | listeners.remove(listener); 81 | } 82 | 83 | @SuppressWarnings("ConstantConditions") 84 | public class EditorImpl implements Editor { 85 | 86 | private final Map newValuesMap = new HashMap<>(); 87 | private boolean shouldClear = false; 88 | 89 | @Override 90 | public Editor putString(final String key, @Nullable final String value) { 91 | newValuesMap.put(key, value); 92 | return this; 93 | } 94 | 95 | @Override 96 | public Editor putStringSet(final String key, @Nullable final Set values) { 97 | newValuesMap.put(key, (values != null) ? new HashSet<>(values) : null); 98 | return this; 99 | } 100 | 101 | @Override 102 | public Editor putInt(final String key, final int value) { 103 | newValuesMap.put(key, value); 104 | return this; 105 | } 106 | 107 | @Override 108 | public Editor putLong(final String key, final long value) { 109 | newValuesMap.put(key, value); 110 | return this; 111 | } 112 | 113 | @Override 114 | public Editor putFloat(final String key, final float value) { 115 | newValuesMap.put(key, value); 116 | return this; 117 | } 118 | 119 | @Override 120 | public Editor putBoolean(final String key, final boolean value) { 121 | newValuesMap.put(key, value); 122 | return this; 123 | } 124 | 125 | @Override 126 | public Editor remove(final String key) { 127 | // 'this' is marker for remove operation 128 | newValuesMap.put(key, this); 129 | return this; 130 | } 131 | 132 | @Override 133 | public Editor clear() { 134 | shouldClear = true; 135 | return this; 136 | } 137 | 138 | @Override 139 | public boolean commit() { 140 | apply(); 141 | return true; 142 | } 143 | 144 | @Override 145 | public void apply() { 146 | clearIfNeeded(); 147 | 148 | final List changedKeys = applyNewValues(); 149 | notifyListeners(changedKeys); 150 | } 151 | 152 | private void clearIfNeeded() { 153 | if (shouldClear) { 154 | shouldClear = false; 155 | preferencesMap.clear(); 156 | } 157 | } 158 | 159 | /** @return changed keys list */ 160 | private List applyNewValues() { 161 | final List changedKeys = new ArrayList<>(); 162 | for (Map.Entry entry : newValuesMap.entrySet()) { 163 | final String key = entry.getKey(); 164 | final Object value = entry.getValue(); 165 | 166 | final boolean isSomethingChanged; 167 | if (isRemoveValue(value)) { 168 | isSomethingChanged = removeIfNeeded(key); 169 | } else { 170 | isSomethingChanged = putValueIfNeeded(key, value); 171 | } 172 | 173 | if (isSomethingChanged) { 174 | changedKeys.add(key); 175 | } 176 | } 177 | 178 | newValuesMap.clear(); 179 | 180 | return changedKeys; 181 | } 182 | 183 | private boolean isRemoveValue(@Nullable final Object value) { 184 | // 'this' is marker for remove operation 185 | return value == this || value == null; 186 | } 187 | 188 | /** @return true if element was removed */ 189 | private boolean removeIfNeeded(@Nullable final String key) { 190 | if (preferencesMap.containsKey(key)) { 191 | preferencesMap.remove(key); 192 | return true; 193 | } else { 194 | return false; 195 | } 196 | } 197 | 198 | /** @return true if element was changed */ 199 | private boolean putValueIfNeeded(@Nullable final String key, @NonNull final Object value) { 200 | if (preferencesMap.containsKey(key)) { 201 | final Object oldValue = preferencesMap.get(key); 202 | if (value.equals(oldValue)) { 203 | return false; 204 | } 205 | } 206 | 207 | preferencesMap.put(key, value); 208 | return true; 209 | } 210 | 211 | private void notifyListeners(@NonNull final List changedKeys) { 212 | for (int i = changedKeys.size() - 1; i >= 0; --i) { 213 | for (OnSharedPreferenceChangeListener listener : listeners) { 214 | listener.onSharedPreferenceChanged(SharedPreferencesMock.this, changedKeys.get(i)); 215 | } 216 | } 217 | changedKeys.clear(); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Shared Preferences Mock 2 | ![Build](https://github.com/IvanShafran/shared-preferences-mock/actions/workflows/code-health-check.yml/badge.svg) 3 | [![codecov](https://codecov.io/gh/IvanShafran/shared-preferences-mock/branch/main/graph/badge.svg)](https://codecov.io/gh/IvanShafran/shared-preferences-mock) 4 | [![Maven Central](https://img.shields.io/maven-central/v/io.github.ivanshafran/shared-preferences-mock.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/io.github.ivanshafran/shared-preferences-mock) 5 | 6 | Shared preferences mock is a lightweight library that lets you increase coverage of unit tests and simplify code for them with one line of code. 7 | 8 | ## Motivation 9 | 10 | Unit test on Android uses a framework mock where every method throws `UnsupportedOperationException` or does nothing depending on your settings in Gradle `testOptions`. 11 | 12 | In unit tests, we mostly test business logic, which often depends on preferences. Therefore you should mock all classes which use `SharedPreferences` inside. It is a lot of boilerplate in tests. Moreover, because of this limitation, we never test preferences class code. However, it also may have bugs. 13 | 14 | ## Under the hood 15 | 16 | Under the hood the library implements `SharedPreferences`, `Context.getSharedPreferences` and `Context.deleteSharedPreferences` in memory using only Java API. Check the differences in the differences paragraph. 17 | 18 | ## Download 19 | 20 | > **Important: changed since 1.2.0** 21 | 22 | Add it in your root build.gradle at the end of repositories: 23 | ```groovy 24 | allprojects { 25 | repositories { 26 | mavenCentral() 27 | } 28 | } 29 | ``` 30 | 31 | Add the dependency: 32 | 33 | ```groovy 34 | dependencies { 35 | // See the actual version above in the maven badge 36 | testImplementation 'io.github.ivanshafran:shared-preferences-mock:x.y.z' 37 | } 38 | ``` 39 | 40 | Enable 1.8 Java compile option if it's not enabled yet: 41 | 42 | ```groovy 43 | android { 44 | compileOptions { 45 | sourceCompatibility JavaVersion.VERSION_1_8 46 | targetCompatibility JavaVersion.VERSION_1_8 47 | } 48 | } 49 | ``` 50 | 51 | ## Release notes 52 | 53 |
Click to see release notes 54 | 55 | Version 1.2.4 56 | 57 | * Master branch to main 58 | 59 | Version 1.2.3 60 | 61 | * First automatic release to maven central 62 | 63 | Version 1.2.1-1.2.2 64 | 65 | * Lost versions 66 | 67 | Version 1.2.0 68 | 69 | * First release to maven central 70 | 71 | Version 1.1 72 | 73 | * Android X support. Thanks to [Neal Sanche](https://github.com/nealsanche) 74 | 75 | Version 1.0 76 | 77 | * First release 78 | 79 |
80 | 81 | ## Usage 82 | 83 | 84 | In most cases, preference class or preference utils depends on `Context`. In unit test instead of real `Context` pass `Context` created by `new SPMockBuilder().createContext()`. 85 | 86 | If you already have `Context` with some mocked methods, you can use `new SPMockBuilder().wrapContext(preconfiguredContext)`. 87 | 88 | If you need raw `SharedPreferences` use `new SPMockBuilder().createSharedPreferences()`. 89 | 90 | If thread safety is necessary for your test, then configure `SPMockBuilder.setThreadSafe(true)` . It is false by default. 91 | 92 | ## Sample 93 | 94 | Full sample code in sample folder. Simplified version is below: 95 | 96 | ```java 97 | public class CounterPreferences { 98 | // ... 99 | private final SharedPreferences sharedPreferences; 100 | public CounterPreferences(@NonNull final Context context) { 101 | sharedPreferences = context.getSharedPreferences(FILENAME, Context.MODE_PRIVATE); 102 | } 103 | 104 | public int getCounter() { 105 | return sharedPreferences.getInt(KEY, 0); 106 | } 107 | // ... 108 | } 109 | 110 | public class ShowMessageLogic { 111 | // ... 112 | public boolean shouldShowMessage() { 113 | return counterPreferences.getCounter() >= 42; 114 | } 115 | } 116 | 117 | public class ShowMessageLogicTest { 118 | 119 | private final SPMockBuilder spMockBuilder = new SPMockBuilder(); 120 | private CounterPreferences counterPreferences; 121 | private ShowMessageLogic showMessageLogic; 122 | 123 | @Before 124 | public void setUp() { 125 | counterPreferences = new CounterPreferences(spMockBuilder.createContext()); 126 | showMessageLogic = new ShowMessageLogic(counterPreferences); 127 | } 128 | 129 | @Test 130 | public void on42CounterItShouldShowMessage() { 131 | counterPreferences.setCounter(42); 132 | Assert.assertTrue(showMessageLogic.shouldShowMessage()); 133 | } 134 | 135 | @Test 136 | public void onLessThan42ItShouldNotShowMessage() { 137 | counterPreferences.setCounter(41); 138 | Assert.assertFalse(showMessageLogic.shouldShowMessage()); 139 | } 140 | } 141 | ``` 142 | 143 | ### Alternatives 144 | 145 | 1. Create `CounterPreferences` interface and implement Java version for test by yourself 146 | 147 | - Doesn't test `CounterPreferences` implementation 148 | - Requires boilerplate code in tests 149 | 150 | 2. Use [Mockito](https://github.com/mockito/mockito) 151 | 152 | - Doesn't test `CounterPreferences` implementation 153 | 154 | 3. Use [Robolectric](https://github.com/robolectric/robolectric) 155 | - Has test startup delay for a few seconds 156 | 157 | 4. Use instrumented test(not a unit test!) 158 | - Requires device for testing 159 | 160 | ## Differences from Android implementation 161 | 162 | 163 | | | Android | Library mock | 164 | |:--------------------------:|--------------------------------------------------------------------------------|-----------------------------------------------------------------------| 165 | | Can be used in unit tests? | No. | Yes. | 166 | | Where it stores data? | In memory and on disk. | In memory. | 167 | | Does it have a global state? | Yes, it synchronizes via files. It is one of the main features. | No, it is better for unit tests. | 168 | | Does API synchronous? | Yes, but some methods schedule asynchronous operations. For example: `apply`. | All methods are synchronous and are performed immediately. | 169 | | Is it thread safe? | Yes. | No by default, because unit tests should be fast as possible for CI. You can enable it with `setThreadSafety(true)` in `SPMockBuilder`.| 170 | | Where are listeners called? | Main thread | Thread on which `apply` or `commit` are called | 171 | | Does it follow docs? | Partly no. `SharedPreferencesImpl` differs from docs in not the most important cases. | Library mimics `SharedPreferencesImpl` differences from docs.| 172 | 173 | ## License 174 | ``` 175 | MIT License 176 | 177 | Copyright (c) 2019 Ivan Shafran 178 | 179 | Permission is hereby granted, free of charge, to any person obtaining a copy 180 | of this software and associated documentation files (the "Software"), to deal 181 | in the Software without restriction, including without limitation the rights 182 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 183 | copies of the Software, and to permit persons to whom the Software is 184 | furnished to do so, subject to the following conditions: 185 | 186 | The above copyright notice and this permission notice shall be included in all 187 | copies or substantial portions of the Software. 188 | 189 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 190 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 191 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 192 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 193 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 194 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 195 | SOFTWARE. 196 | ``` 197 | -------------------------------------------------------------------------------- /sharedpreferencesmock/src/main/java/com/github/ivanshafran/sharedpreferencesmock/internal/ContextMock.java: -------------------------------------------------------------------------------- 1 | package com.github.ivanshafran.sharedpreferencesmock.internal; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.BroadcastReceiver; 5 | import android.content.ComponentCallbacks; 6 | import android.content.ComponentName; 7 | import android.content.ContentResolver; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.IntentFilter; 11 | import android.content.IntentSender; 12 | import android.content.ServiceConnection; 13 | import android.content.SharedPreferences; 14 | import android.content.pm.ApplicationInfo; 15 | import android.content.pm.PackageManager; 16 | import android.content.res.AssetManager; 17 | import android.content.res.Configuration; 18 | import android.content.res.Resources; 19 | import android.database.DatabaseErrorHandler; 20 | import android.database.sqlite.SQLiteDatabase; 21 | import android.graphics.Bitmap; 22 | import android.graphics.drawable.Drawable; 23 | import android.net.Uri; 24 | import android.os.Bundle; 25 | import android.os.Handler; 26 | import android.os.Looper; 27 | import android.os.UserHandle; 28 | import androidx.annotation.NonNull; 29 | import androidx.annotation.Nullable; 30 | import android.view.Display; 31 | 32 | import java.io.File; 33 | import java.io.FileInputStream; 34 | import java.io.FileOutputStream; 35 | import java.io.InputStream; 36 | import java.util.concurrent.Executor; 37 | 38 | @SuppressLint("WrongConstant") 39 | public class ContextMock extends Context { 40 | 41 | @Override 42 | public SharedPreferences getSharedPreferences(final String name, final int mode) { 43 | throw new UnsupportedOperationException(); 44 | } 45 | 46 | @Override 47 | public boolean deleteSharedPreferences(final String name) { 48 | throw new UnsupportedOperationException(); 49 | } 50 | 51 | @Override 52 | public boolean moveSharedPreferencesFrom(final Context sourceContext, final String name) { 53 | throw new UnsupportedOperationException(); 54 | } 55 | 56 | @Override 57 | public AssetManager getAssets() { 58 | throw new UnsupportedOperationException(); 59 | } 60 | 61 | @Override 62 | public Resources getResources() { 63 | throw new UnsupportedOperationException(); 64 | } 65 | 66 | @Override 67 | public PackageManager getPackageManager() { 68 | throw new UnsupportedOperationException(); 69 | } 70 | 71 | @Override 72 | public ContentResolver getContentResolver() { 73 | throw new UnsupportedOperationException(); 74 | } 75 | 76 | @Override 77 | public Looper getMainLooper() { 78 | throw new UnsupportedOperationException(); 79 | } 80 | 81 | @Override 82 | public Context getApplicationContext() { 83 | throw new UnsupportedOperationException(); 84 | } 85 | 86 | @Override 87 | public void setTheme(final int resid) { 88 | throw new UnsupportedOperationException(); 89 | } 90 | 91 | @Override 92 | public Resources.Theme getTheme() { 93 | throw new UnsupportedOperationException(); 94 | } 95 | 96 | @Override 97 | public ClassLoader getClassLoader() { 98 | throw new UnsupportedOperationException(); 99 | } 100 | 101 | @Override 102 | public String getPackageName() { 103 | throw new UnsupportedOperationException(); 104 | } 105 | 106 | @Override 107 | public ApplicationInfo getApplicationInfo() { 108 | throw new UnsupportedOperationException(); 109 | } 110 | 111 | @Override 112 | public String getPackageResourcePath() { 113 | throw new UnsupportedOperationException(); 114 | } 115 | 116 | @Override 117 | public String getPackageCodePath() { 118 | throw new UnsupportedOperationException(); 119 | } 120 | 121 | @Override 122 | public FileInputStream openFileInput(final String name) { 123 | throw new UnsupportedOperationException(); 124 | } 125 | 126 | @Override 127 | public FileOutputStream openFileOutput(final String name, final int mode) { 128 | throw new UnsupportedOperationException(); 129 | } 130 | 131 | @Override 132 | public boolean deleteFile(final String name) { 133 | throw new UnsupportedOperationException(); 134 | } 135 | 136 | @Override 137 | public File getFileStreamPath(final String name) { 138 | throw new UnsupportedOperationException(); 139 | } 140 | 141 | @Override 142 | public File getDataDir() { 143 | throw new UnsupportedOperationException(); 144 | } 145 | 146 | @Override 147 | public File getFilesDir() { 148 | throw new UnsupportedOperationException(); 149 | } 150 | 151 | @Override 152 | public File getNoBackupFilesDir() { 153 | throw new UnsupportedOperationException(); 154 | } 155 | 156 | @Nullable 157 | @Override 158 | public File getExternalFilesDir(@Nullable final String type) { 159 | throw new UnsupportedOperationException(); 160 | } 161 | 162 | @Override 163 | public File[] getExternalFilesDirs(final String type) { 164 | throw new UnsupportedOperationException(); 165 | } 166 | 167 | @Override 168 | public File getObbDir() { 169 | throw new UnsupportedOperationException(); 170 | } 171 | 172 | @Override 173 | public File[] getObbDirs() { 174 | throw new UnsupportedOperationException(); 175 | } 176 | 177 | @Override 178 | public File getCacheDir() { 179 | throw new UnsupportedOperationException(); 180 | } 181 | 182 | @Override 183 | public File getCodeCacheDir() { 184 | throw new UnsupportedOperationException(); 185 | } 186 | 187 | @Nullable 188 | @Override 189 | public File getExternalCacheDir() { 190 | throw new UnsupportedOperationException(); 191 | } 192 | 193 | @Override 194 | public File[] getExternalCacheDirs() { 195 | throw new UnsupportedOperationException(); 196 | } 197 | 198 | @Override 199 | public File[] getExternalMediaDirs() { 200 | throw new UnsupportedOperationException(); 201 | } 202 | 203 | @Override 204 | public String[] fileList() { 205 | throw new UnsupportedOperationException(); 206 | } 207 | 208 | @Override 209 | public File getDir(final String name, final int mode) { 210 | throw new UnsupportedOperationException(); 211 | } 212 | 213 | @Override 214 | public SQLiteDatabase openOrCreateDatabase( 215 | final String name, 216 | final int mode, 217 | final SQLiteDatabase.CursorFactory factory 218 | ) { 219 | throw new UnsupportedOperationException(); 220 | } 221 | 222 | @Override 223 | public SQLiteDatabase openOrCreateDatabase( 224 | final String name, 225 | final int mode, 226 | final SQLiteDatabase.CursorFactory factory, 227 | @Nullable final DatabaseErrorHandler errorHandler 228 | ) { 229 | throw new UnsupportedOperationException(); 230 | } 231 | 232 | @Override 233 | public boolean moveDatabaseFrom(final Context sourceContext, final String name) { 234 | throw new UnsupportedOperationException(); 235 | } 236 | 237 | @Override 238 | public boolean deleteDatabase(final String name) { 239 | throw new UnsupportedOperationException(); 240 | } 241 | 242 | @Override 243 | public File getDatabasePath(final String name) { 244 | throw new UnsupportedOperationException(); 245 | } 246 | 247 | @Override 248 | public String[] databaseList() { 249 | throw new UnsupportedOperationException(); 250 | } 251 | 252 | @Override 253 | public Drawable getWallpaper() { 254 | throw new UnsupportedOperationException(); 255 | } 256 | 257 | @Override 258 | public Drawable peekWallpaper() { 259 | throw new UnsupportedOperationException(); 260 | } 261 | 262 | @Override 263 | public int getWallpaperDesiredMinimumWidth() { 264 | throw new UnsupportedOperationException(); 265 | } 266 | 267 | @Override 268 | public int getWallpaperDesiredMinimumHeight() { 269 | throw new UnsupportedOperationException(); 270 | } 271 | 272 | @Override 273 | public void setWallpaper(final Bitmap bitmap) { 274 | throw new UnsupportedOperationException(); 275 | } 276 | 277 | @Override 278 | public void setWallpaper(final InputStream data) { 279 | throw new UnsupportedOperationException(); 280 | } 281 | 282 | @Override 283 | public void clearWallpaper() { 284 | throw new UnsupportedOperationException(); 285 | } 286 | 287 | @Override 288 | public void startActivity(final Intent intent) { 289 | throw new UnsupportedOperationException(); 290 | } 291 | 292 | @Override 293 | public void startActivity(final Intent intent, @Nullable final Bundle options) { 294 | throw new UnsupportedOperationException(); 295 | } 296 | 297 | @Override 298 | public void startActivities(final Intent[] intents) { 299 | throw new UnsupportedOperationException(); 300 | } 301 | 302 | @Override 303 | public void startActivities(final Intent[] intents, final Bundle options) { 304 | throw new UnsupportedOperationException(); 305 | } 306 | 307 | @Override 308 | public void startIntentSender( 309 | final IntentSender intent, 310 | @Nullable final Intent fillInIntent, 311 | final int flagsMask, 312 | final int flagsValues, 313 | final int extraFlags 314 | ) { 315 | throw new UnsupportedOperationException(); 316 | } 317 | 318 | @Override 319 | public void startIntentSender( 320 | final IntentSender intent, 321 | @Nullable final Intent fillInIntent, 322 | final int flagsMask, 323 | final int flagsValues, 324 | final int extraFlags, 325 | @Nullable final Bundle options 326 | ) { 327 | throw new UnsupportedOperationException(); 328 | } 329 | 330 | @Override 331 | public void sendBroadcast(final Intent intent) { 332 | throw new UnsupportedOperationException(); 333 | } 334 | 335 | @Override 336 | public void sendBroadcast(final Intent intent, @Nullable final String receiverPermission) { 337 | throw new UnsupportedOperationException(); 338 | } 339 | 340 | @Override 341 | public void sendOrderedBroadcast(final Intent intent, @Nullable final String receiverPermission) { 342 | throw new UnsupportedOperationException(); 343 | } 344 | 345 | @Override 346 | public void sendOrderedBroadcast( 347 | @NonNull final Intent intent, 348 | @Nullable final String receiverPermission, 349 | @Nullable final BroadcastReceiver resultReceiver, 350 | @Nullable final Handler scheduler, 351 | final int initialCode, 352 | @Nullable final String initialData, 353 | @Nullable final Bundle initialExtras 354 | ) { 355 | throw new UnsupportedOperationException(); 356 | } 357 | 358 | @Override 359 | public void sendBroadcastAsUser(final Intent intent, final UserHandle user) { 360 | throw new UnsupportedOperationException(); 361 | } 362 | 363 | @Override 364 | public void sendBroadcastAsUser( 365 | final Intent intent, 366 | final UserHandle user, 367 | @Nullable final String receiverPermission 368 | ) { 369 | throw new UnsupportedOperationException(); 370 | } 371 | 372 | @Override 373 | public void sendOrderedBroadcastAsUser( 374 | final Intent intent, 375 | final UserHandle user, 376 | @Nullable final String receiverPermission, 377 | final BroadcastReceiver resultReceiver, 378 | @Nullable final Handler scheduler, 379 | final int initialCode, 380 | @Nullable final String initialData, 381 | @Nullable final Bundle initialExtras 382 | ) { 383 | throw new UnsupportedOperationException(); 384 | } 385 | 386 | @Override 387 | public void sendStickyBroadcast(final Intent intent) { 388 | throw new UnsupportedOperationException(); 389 | } 390 | 391 | @Override 392 | public void sendStickyOrderedBroadcast( 393 | final Intent intent, 394 | final BroadcastReceiver resultReceiver, 395 | @Nullable final Handler scheduler, 396 | final int initialCode, 397 | @Nullable final String initialData, 398 | @Nullable final Bundle initialExtras 399 | ) { 400 | throw new UnsupportedOperationException(); 401 | } 402 | 403 | @Override 404 | public void removeStickyBroadcast(final Intent intent) { 405 | throw new UnsupportedOperationException(); 406 | } 407 | 408 | @Override 409 | public void sendStickyBroadcastAsUser(final Intent intent, final UserHandle user) { 410 | throw new UnsupportedOperationException(); 411 | } 412 | 413 | @Override 414 | public void sendStickyOrderedBroadcastAsUser( 415 | final Intent intent, 416 | final UserHandle user, 417 | final BroadcastReceiver resultReceiver, 418 | @Nullable final Handler scheduler, 419 | final int initialCode, 420 | @Nullable final String initialData, 421 | @Nullable final Bundle initialExtras 422 | ) { 423 | throw new UnsupportedOperationException(); 424 | } 425 | 426 | @Override 427 | public void removeStickyBroadcastAsUser(final Intent intent, final UserHandle user) { 428 | throw new UnsupportedOperationException(); 429 | } 430 | 431 | @Nullable 432 | @Override 433 | public Intent registerReceiver(@Nullable final BroadcastReceiver receiver, final IntentFilter filter) { 434 | throw new UnsupportedOperationException(); 435 | } 436 | 437 | @Nullable 438 | @Override 439 | public Intent registerReceiver( 440 | @Nullable final BroadcastReceiver receiver, 441 | final IntentFilter filter, 442 | final int flags 443 | ) { 444 | throw new UnsupportedOperationException(); 445 | } 446 | 447 | @Nullable 448 | @Override 449 | public Intent registerReceiver( 450 | final BroadcastReceiver receiver, 451 | final IntentFilter filter, 452 | @Nullable final String broadcastPermission, 453 | @Nullable final Handler scheduler 454 | ) { 455 | throw new UnsupportedOperationException(); 456 | } 457 | 458 | @Nullable 459 | @Override 460 | public Intent registerReceiver( 461 | final BroadcastReceiver receiver, 462 | final IntentFilter filter, 463 | @Nullable final String broadcastPermission, 464 | @Nullable final Handler scheduler, 465 | final int flags 466 | ) { 467 | throw new UnsupportedOperationException(); 468 | } 469 | 470 | @Override 471 | public void unregisterReceiver(final BroadcastReceiver receiver) { 472 | throw new UnsupportedOperationException(); 473 | } 474 | 475 | @Nullable 476 | @Override 477 | public ComponentName startService(final Intent service) { 478 | throw new UnsupportedOperationException(); 479 | } 480 | 481 | @Nullable 482 | @Override 483 | public ComponentName startForegroundService(final Intent service) { 484 | throw new UnsupportedOperationException(); 485 | } 486 | 487 | @Override 488 | public boolean stopService(final Intent service) { 489 | throw new UnsupportedOperationException(); 490 | } 491 | 492 | @Override 493 | public boolean bindService(final Intent service, @NonNull final ServiceConnection conn, final int flags) { 494 | throw new UnsupportedOperationException(); 495 | } 496 | 497 | @Override 498 | public void unbindService(@NonNull final ServiceConnection conn) { 499 | throw new UnsupportedOperationException(); 500 | } 501 | 502 | @Override 503 | public boolean startInstrumentation( 504 | @NonNull final ComponentName className, 505 | @Nullable final String profileFile, 506 | @Nullable final Bundle arguments 507 | ) { 508 | throw new UnsupportedOperationException(); 509 | } 510 | 511 | @Override 512 | public Object getSystemService(@NonNull final String name) { 513 | throw new UnsupportedOperationException(); 514 | } 515 | 516 | @Nullable 517 | @Override 518 | public String getSystemServiceName(@NonNull final Class serviceClass) { 519 | throw new UnsupportedOperationException(); 520 | } 521 | 522 | @Override 523 | public int checkPermission(@NonNull final String permission, final int pid, final int uid) { 524 | throw new UnsupportedOperationException(); 525 | } 526 | 527 | @Override 528 | public int checkCallingPermission(@NonNull final String permission) { 529 | throw new UnsupportedOperationException(); 530 | } 531 | 532 | @Override 533 | public int checkCallingOrSelfPermission(@NonNull final String permission) { 534 | throw new UnsupportedOperationException(); 535 | } 536 | 537 | @Override 538 | public int checkSelfPermission(@NonNull final String permission) { 539 | throw new UnsupportedOperationException(); 540 | } 541 | 542 | @Override 543 | public void enforcePermission( 544 | @NonNull final String permission, 545 | final int pid, 546 | final int uid, 547 | @Nullable final String message 548 | ) { 549 | throw new UnsupportedOperationException(); 550 | } 551 | 552 | @Override 553 | public void enforceCallingPermission(@NonNull final String permission, @Nullable final String message) { 554 | throw new UnsupportedOperationException(); 555 | } 556 | 557 | @Override 558 | public void enforceCallingOrSelfPermission(@NonNull final String permission, @Nullable final String message) { 559 | throw new UnsupportedOperationException(); 560 | } 561 | 562 | @Override 563 | public void grantUriPermission(final String toPackage, final Uri uri, final int modeFlags) { 564 | throw new UnsupportedOperationException(); 565 | } 566 | 567 | @Override 568 | public void revokeUriPermission(final Uri uri, final int modeFlags) { 569 | throw new UnsupportedOperationException(); 570 | } 571 | 572 | @Override 573 | public void revokeUriPermission(final String toPackage, final Uri uri, final int modeFlags) { 574 | throw new UnsupportedOperationException(); 575 | } 576 | 577 | @Override 578 | public int checkUriPermission(final Uri uri, final int pid, final int uid, final int modeFlags) { 579 | throw new UnsupportedOperationException(); 580 | } 581 | 582 | @Override 583 | public int checkCallingUriPermission(final Uri uri, final int modeFlags) { 584 | throw new UnsupportedOperationException(); 585 | } 586 | 587 | @Override 588 | public int checkCallingOrSelfUriPermission(final Uri uri, final int modeFlags) { 589 | throw new UnsupportedOperationException(); 590 | } 591 | 592 | @Override 593 | public int checkUriPermission( 594 | @Nullable final Uri uri, 595 | @Nullable final String readPermission, 596 | @Nullable final String writePermission, 597 | final int pid, 598 | final int uid, 599 | final int modeFlags 600 | ) { 601 | throw new UnsupportedOperationException(); 602 | } 603 | 604 | @Override 605 | public void enforceUriPermission( 606 | final Uri uri, 607 | final int pid, 608 | final int uid, 609 | final int modeFlags, 610 | final String message 611 | ) { 612 | throw new UnsupportedOperationException(); 613 | } 614 | 615 | @Override 616 | public void enforceCallingUriPermission(final Uri uri, final int modeFlags, final String message) { 617 | throw new UnsupportedOperationException(); 618 | } 619 | 620 | @Override 621 | public void enforceCallingOrSelfUriPermission(final Uri uri, final int modeFlags, final String message) { 622 | throw new UnsupportedOperationException(); 623 | } 624 | 625 | @Override 626 | public void enforceUriPermission( 627 | @Nullable final Uri uri, 628 | @Nullable final String readPermission, 629 | @Nullable final String writePermission, 630 | final int pid, 631 | final int uid, 632 | final int modeFlags, 633 | @Nullable final String message 634 | ) { 635 | throw new UnsupportedOperationException(); 636 | } 637 | 638 | @Override 639 | public Context createPackageContext(final String packageName, final int flags) { 640 | throw new UnsupportedOperationException(); 641 | } 642 | 643 | @Override 644 | public Context createContextForSplit(final String splitName) { 645 | throw new UnsupportedOperationException(); 646 | } 647 | 648 | @Override 649 | public Context createConfigurationContext(@NonNull final Configuration overrideConfiguration) { 650 | throw new UnsupportedOperationException(); 651 | } 652 | 653 | @Override 654 | public Context createDisplayContext(@NonNull final Display display) { 655 | throw new UnsupportedOperationException(); 656 | } 657 | 658 | @Override 659 | public Context createDeviceProtectedStorageContext() { 660 | throw new UnsupportedOperationException(); 661 | } 662 | 663 | @Override 664 | public boolean isDeviceProtectedStorage() { 665 | throw new UnsupportedOperationException(); 666 | } 667 | 668 | @Override 669 | public boolean isRestricted() { 670 | throw new UnsupportedOperationException(); 671 | } 672 | 673 | @Override 674 | public Executor getMainExecutor() { 675 | throw new UnsupportedOperationException(); 676 | } 677 | 678 | @Override 679 | public void registerComponentCallbacks(final ComponentCallbacks callback) { 680 | throw new UnsupportedOperationException(); 681 | } 682 | 683 | @Override 684 | public void unregisterComponentCallbacks(final ComponentCallbacks callback) { 685 | throw new UnsupportedOperationException(); 686 | } 687 | } 688 | --------------------------------------------------------------------------------