A SharedPreference "injection" library for Android

Overview

Android Arsenal

PreferenceBinder

A SharedPreferences binding library for Android. Using annotation processing, this library makes it easy to load SharedPreferences values and listen for changes.

How to Use

Basic Usage

Use the @BindPref annotation to retrieve, initialize, and listen to changes in ("bind") preference values.

public class MainActivity extends Activity {

    @BindPref("my_preference_key") String valueOfPreference;

    @BindPref("my_preference_key") void initializeForPreferenceValue(String valueOfPreference) {
        // do something with the value
        ...
    }

    @Override public void onCreate(Bundle inState) {
        PreferenceBinder.bind(this);
    }

    @Override public void onDestroy() {
        PreferenceBinder.unbind(this);
    }

}

As soon as PreferenceBinder.bind() is called, preference values are loaded from your default SharedPreferences file and assigned to the annotated fields, and passed as the parameter to annotated methods. Whenever the preference value changes, annotated fields are re-assigned, and annotated methods are called again with the new preference value.

Be sure to match the field types and method parameter types with the type of value stored for the preference key. This is not checked at compile time, and may cause runtime exceptions.

To use a non-default SharedPreferences file, you can specify the name of the file, like so:

PreferenceBinder.bind(this, "prefs_file_name");

Advanced Usage

You may specify more than one preference key when annotating methods with @BindPref. In this case, the method will be called when the value for any one of the specified keys changes. For example:

@BindPref({"show_full_names", "use_small_icons"})
void refreshList() {
    adapter.notifyDataSetChanged();
}

Method bindings with more than one preference key do not supply the new value of the preference. But if used in combination with field bindings, the method will always be called after the new preference values have been assigned to any annotated fields so that they can be used inside the method call.

If you only want to initialize your preference values (and not bother listening for changes), you can do so with the listen flag. Altenatively, you can disable initialization with the init flag.

@BindPref(value = "use_small_icons", listen = false)
void initUseSmallIcons(boolean useSmallIcons) {
    // Do something with the value
    // ...
}

@BindPref(value = "show_full_names", init = false)
void onShowFullNamesChanged(boolean showFullNames) {
    // Do something with the value
    // ...
}

Default Values

To specify default values for preference keys, use the @PreferenceDefault annotation on static field containing the default value, like so:

@PreferenceDefault("my_preference_key") public static String MY_PREFERENCE_DEFAULT = "Unknown";

@BindPref("my_preference_key") void updateForValue(String valueOfPreference) {
    // do something with the value
    // ...
}

In the above example, PreferenceBinder will call updateForValue(MY_PREFERENCE_DEFAULT) if no value is set for "my_preference_key" on initialization, or if the value for the given key is removed (with "listening" enabled).

Default values apply to your entire application, so you don't need to specify them in each class. You might find it convenient to assign them all in a single utility class.

Widget Binding

Preference values can also be bound directly into some standard Android widgets.

For example, in the following code will automatically load the preference value for the key "sensitivity" and apply it to the SeekBar through its setProgress method.

@BindPref(value = "sensitivity", bindTo = WidgetBindingType.SEEKBAR_PROGRESS)
SeekBar sensitivity;

In addition to loading the preference value into the widget, PreferenceBinder will also listen for changes to the SeekBar's progress value (from user input) and save the new value back into your SharedPreferences file for the given preference key!

The following table outlines the widget binding types that are currently supported. If you would like to see a binding type included in this library, please post an issue for the feature request.

"bindTo" type Widget type Method called Saves user changes?
ASSIGN (default) - = no
ACTIVATED View setActivated no
ENABLED View setEnabled no
SELECTED View setSelected no
VISIBILITY View setVisibility (View.VISIBLE or View.GONE) no
CHECKED CompoundButton setChecked yes
TEXT TextView setText no
SEEKBAR_PROGRESS SeekBar setProgress yes
PROGRESS ProgressBar setProgress no
MAX_PROGRESS ProgressBar setMax no

Build Configuration

Add the following line to the gradle dependencies for your module.

compile 'me.denley.preferenceinjector:PreferenceInjector:3.0.2'

If you are using any other annotation processors in your application (e.g. Dagger, ButterKnife, etc.) then you will also need to add the following to your module's build.gradle file:

android {
    packagingOptions {
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }
}

ProGuard

When using ProGuard, you need to specify that generated classes should be kept, and that annotated fields and methods should not be renamed. To achieve these criteria, the following lines can be added to your ProGuard configuration:

-keep class me.denley.preferencebinder.** { *; }
-dontwarn me.denley.preferencebinder.internal.**
-keep class **$$SharedPreferenceBinder { *; }

-keepclasseswithmembernames class * {
    @me.denley.preferencebinder.* <fields>;
}

-keepclasseswithmembernames class * {
    @me.denley.preferencebinder.* <methods>;
}

License

Copyright 2015 Denley Bihari

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Annotation processor structure adapted from Butter Knife (Copyright 2013 Jake Wharton).

Comments
  • value as int (string resource)

    value as int (string resource)

    Is it possible to use it with a string resource as value for 'value' ? I mean like this

    @InjectPreference(value = R.string.SP_OFFSET_AUDIO, listen = true)

    feature 
    opened by mlostekk 12
  • The word injection should be in quotes.

    The word injection should be in quotes.

    ButterKnife purports to be an injection library solely as a joke (poking fun at RoboGuice). As such, its usages of the word injection are always in quotes. It has nothing to do with dependency injection. In fact, the next major version will move from @InjectView to @BindView or @FindVIew and I will be removing all usages of the word injection.

    While I don't expect you to change this library in that same fashion, putting injection in quotes at least covers your ass for the fact that this or any of these libraries (including Butter Knife) are not true injection.

    opened by JakeWharton 5
  • Build failed with NPE

    Build failed with NPE

    I'm getting a build failure with this library. Relevant section of stacktrace:

    Caused by: java.lang.NullPointerException at me.denley.preferencebinder.internal.BindingCallCodeAnalyzer.visitMethod(BindingCallCodeAnalyzer.java:31) at me.denley.preferencebinder.internal.BindingCallCodeAnalyzer.visitMethod(BindingCallCodeAnalyzer.java:13) at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:800) at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:68) at com.sun.source.util.TreeScanner.scanAndReduce(TreeScanner.java:81) at com.sun.source.util.TreeScanner.scan(TreeScanner.java:91) at com.sun.source.util.TreeScanner.scanAndReduce(TreeScanner.java:99) at com.sun.source.util.TreeScanner.visitClass(TreeScanner.java:133) at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:720) at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:50) at me.denley.preferencebinder.internal.PreferenceBinderProcessor.classMakesStatementCall(PreferenceBinderProcessor.java:334) at me.denley.preferencebinder.internal.PreferenceBinderProcessor.checkForBindingCalls(PreferenceBinderProcessor.java:310) at me.denley.preferencebinder.internal.PreferenceBinderProcessor.findAndParseAnnotations(PreferenceBinderProcessor.java:100) at me.denley.preferencebinder.internal.PreferenceBinderProcessor.process(PreferenceBinderProcessor.java:76) at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794) at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705) at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91) at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035) at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176) at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170) at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:856) at com.sun.tools.javac.main.Main.compile(Main.java:523) ... 66 more

    bug 
    opened by rbtr 4
  • InjectPreference not invoking for custom NumberPickerPreference

    InjectPreference not invoking for custom NumberPickerPreference

    InjectPreference method is not invoked when used with custom preference. It is invoked for built-in preferences right when settings fragment is loaded. No issues with OnPreferenceChange. It is invoked for custom preferences as well. Am I not using it correctly?

    public class SettingsFragment extends PreferenceFragment 
    {
        private static final String batteryLevelPrefKey = "pref_battery_level";
    
    @InjectPreference(value = batteryLevelPrefKey, listen = true)
        void onBatteryLevelPreferenceChanged(int level)
        {
            Preference pref = findPreference(batteryLevelPrefKey);
            pref.setSummary(String.valueOf(level));
        }
    }
    

    Custom preference used ->

    package com.techlycans.batteryfullalert;
    
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.SharedPreferences;
    import android.content.res.TypedArray;
    import android.preference.DialogPreference;
    import android.preference.PreferenceManager;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.NumberPicker;
    
    public class NumberPickerPreference extends DialogPreference
    {
        private static final int max = 100;
        private static final int min = 1;
        private static final int defVal = 100;
        NumberPicker picker;
        Integer initialValue;
    
        public NumberPickerPreference(Context context, AttributeSet attrs)
        {
            super(context, attrs);
        }
    
        @Override
        protected void onBindDialogView(View view)
        {
            super.onBindDialogView(view);
            this.picker = (NumberPicker) view.findViewById(R.id.number_picker);
            // TODO this should be an XML parameter:
            picker.setMaxValue(max);
            picker.setMinValue(min);
            if (this.initialValue != null)
                picker.setValue(initialValue);
        }
    
        @Override
        public void onClick(DialogInterface dialog, int which)
        {
            super.onClick(dialog, which);
            if (which == DialogInterface.BUTTON_POSITIVE)
            {
                picker.clearFocus(); //this is imp
                this.initialValue = picker.getValue();
                persistInt(initialValue);
                callChangeListener(initialValue);
            }
        }
    
        @Override
        protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
        {
            int def = (defaultValue instanceof Number) ? (Integer) defaultValue
                    : (defaultValue != null) ? Integer.parseInt(defaultValue.toString()) : defVal;
            if (restorePersistedValue)
            {
                this.initialValue = getPersistedInt(def);
            }
            else
                this.initialValue = (Integer) defaultValue;
        }
    
        @Override
        protected Object onGetDefaultValue(TypedArray a, int index)
        {
            return a.getInt(index, defVal);
        }
    }
    
    support 
    opened by adaneam 4
  • PreferenceDefault should apply globally

    PreferenceDefault should apply globally

    @PreferenceDefault annotated fields should apply the field as a default value for the entire application, not just for the class it's contained inside.

    This implies a strict requirement that they have public static modifiers.

    We should also add a mechanism for specifying the preference file that they apply to.

    e.g.:

    @PreferenceDefault(value = "notifications_led", prefsFile = "notifications")
    public static final boolean USE_NOTIFICATION_LED = true;
    
    enhancement 
    opened by denley 1
  • Support String resources for preference keys

    Support String resources for preference keys

    @InjectPreferenceStringRes(R.string.pref_foo_bar)
    String preferenceValue;
    
    @PreferenceDefaultFromRes(R.string.pref_notification_led)
    public static final int USE_NOTIFICATION_LED = R.bool.use_notification_led;
    
    wontfix feature 
    opened by denley 1
  • Issue when Butterknife and PrefenceInjector are used in same project

    Issue when Butterknife and PrefenceInjector are used in same project

    I get following error when using both Butterknife and PrefenceInjector

    Error:Execution failed for task ':app:packageDebug'.

    Duplicate files copied in APK META-INF/services/javax.annotation.processing.Processor File 1: D:\Users\admin.gradle\caches\modules-2\files-2.1\com.jakewharton\butterknife\6.1.0\63735f48b82bcd24cdd33821342428252eb1ca5a\butterknife-6.1.0.jar File 2: E:\TestApp\app\build\intermediates\exploded-aar\me.denley.preferenceinjector\PreferenceInjector\2.2.1\jars\classes.jar

    opened by adaneam 1
  • Build Failure

    Build Failure

    I've encountered the following build error when adding PreferenceInjector to one of my apps:

    com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\Program Files\Java\jdk1.8.0_05\bin\java.exe'' finished with non-zero exit value 2

    dependencies {
        // *Flurry analytics from jar file*
        compile project(':common')
        wearApp project(':wear')
        compile 'com.android.support:appcompat-v7:21.0.3'
        compile 'com.android.support:cardview-v7:21.0.3'
    
        // It works without this line
        compile 'me.denley.preferenceinjector:PreferenceInjector:2.2.0'
    
        compile 'me.denley.wearprefs:WearPrefs:1.1.2'
        compile 'me.denley.courier:courier:0.5.0'
        compile 'com.jakewharton:butterknife:6.1.0'
    
        compile 'co.52inc:attributr:1.0.0'
        compile 'fr.nicolaspomepuy:discreetapprate:2.0.3@aar'
        compile 'fr.nicolaspomepuy.androidwearcrashreport:crashreport-mobile:0.4@aar'
    
        compile('com.crashlytics.sdk.android:crashlytics:2.2.2@aar') {
            transitive = true;
        }
        aarLinkJavadoc 'com.crashlytics.sdk.android:crashlytics:2.2.2:javadoc@jar'
    }
    
    wontfix 
    opened by denley 1
  • Inheritance support

    Inheritance support

    Currently, if a target class is extended it will no longer "inject" preferences into the parent class. Inheritance should be supported, and all classes in the hierarchy should be "injected".

    bug 
    opened by denley 1
  • @AfterPreferenceInjection

    @AfterPreferenceInjection

    A method annotation to specify a method (or methods) should be called after all initialization has occurred.

    This is a convenient feature when wanting to initialize a component based on multiple preference values (e.g. a Button that is disabled if either of two preferences is false).

    wontfix 
    opened by denley 1
  • Allow

    Allow "injection" directly into certain widgets

    This could be done in a few ways:

    By inferring the type from the widget type (only one "injection" type per widget type):

    @BindPref("username")
    TextView loggedInUser;
    

    Or maybe by using different annotations for each type:

    @BindBackgroundColorPreference("background_color")
    View rootView;
    

    Or maybe by manually specifying the type:

    @BindPref(value = "background_color", bindTo = BACKGROUND_COLOR)
    View rootView;
    
    enhancement 
    opened by denley 1
  • Improve Samples

    Improve Samples

    The samples have become unrealistic, as they have been used to test the correct implementation of the library, rather than as supporting documentation.

    The samples should reflect the most common real-world use cases, and promote best practices of usage.

    documentation 
    opened by denley 0
  • Saving/Loading complex objects

    Saving/Loading complex objects

    It might be useful to support saving complex/structured objects that are composed of multiple preference keys.

    @PrefType public class User {
    
        String username;
        String email;
        String phone;
        int age;
        @PrefKey("is_verified") boolean verified;
    
    }
    

    or

    @PrefType public interface User {
    
        String getUsername();
        void setUsername(String username);
    
        String getEmail();
        void setEmail(String email);
    
        String getPhone();
        void setPhone(String phone);
    
        int getAge();
        void setAge(int age);
    
        @PrefKey("is_verified") 
        boolean getVerified();
        void setVerified(boolean verified);
    
    }
    

    Usage:

    @BindPrefType(listen = false) User user;
    @BindPrefType(init = false) void bindToUser(User user) { ... }
    @BindPrefType({User.class, Settings.class}) void onUserOrSettingsChanged() { ... }
    
    PreferenceBinder.save(context, user);
    PreferenceBinder.save(context, "prefs_file", user);
    
    feature 
    opened by denley 1
  • Check for `PreferenceInjector.inject` call at compile time

    Check for `PreferenceInjector.inject` call at compile time

    It would improve UX to check that the user has properly called PreferenceInjector.inject and PreferenceInjector.stopListening (if necessary) in the target class if there are annotations present.

    This may not actually be possible, especially when dealing with inheritance.

    enhancement 
    opened by denley 2
  • Exclude javax.* from release packages

    Exclude javax.* from release packages

    The processor requires some classes in javax.* to compile, which are included in the source. However, these classes don't need to be included in the release packages, and add unnecessary size.

    enhancement help wanted 
    opened by denley 2
Owner
Denley Bihari
Denley Bihari
Lightweight, minimalistic dependency injection library for Kotlin & Android

‼️ This project is in maintenance mode and not actively developed anymore. For more information read this statement. ‼️ Katana Katana is a lightweight

REWE Digital GmbH 179 Nov 27, 2022
A multi-purpose library containing view injection and threading for Android using annotations

SwissKnife A multi-purpose Groovy library containing view injection and threading for Android using annotations. It's based on both ButterKnife and An

Jorge Martin Espinosa 251 Nov 25, 2022
:syringe: Transfuse - A Dependency Injection and Integration framework for Google Android

Transfuse Transfuse is a Java Dependency Injection (DI) and integration library geared specifically for the Google Android API. There are several key

John Ericksen 224 Nov 28, 2022
Guice (pronounced 'juice') is a lightweight dependency injection framework for Java 6 and above, brought to you by Google.

Guice Latest release: 5.0.1 Documentation: User Guide, 5.0.1 javadocs, Latest javadocs Continuous Integration: Mailing Lists: User Mailing List Licens

Google 11.7k Jan 1, 2023
Painless Kotlin Dependency Injection

KOtlin DEpendency INjection Kodein-DI is a very simple and yet very useful dependency retrieval container. it is very easy to use and configure. Kodei

null 2.9k Jan 1, 2023
Koin - a pragmatic lightweight dependency injection framework for Kotlin

What is KOIN? - https://insert-koin.io A pragmatic lightweight dependency injection framework for Kotlin developers. Koin is a DSL, a light container

insert-koin.io 7.8k Jan 8, 2023
The dependency injection Dagger basics and the concepts are demonstrated in different branches

In this project we will look at the dependency injection Dagger basics and the concepts are demonstrated in different branches What is an Dependency?

Lloyd Dcosta 6 Dec 16, 2021
Simple Android Library, that provides easy way to start the Activities with arguments.

Warning: Library is not maintained anymore. If you want to take care of this library, propose it via Pull Request. It needs adjustmensts for newer ver

Marcin Moskała 429 Dec 15, 2022
DependencyProperty is a dependency resolution library by Delegated Property.

DependencyProperty is a dependency resolution library by Delegated Property. Overview DependencyProperty is simple in defining and

wada811 10 Dec 31, 2022
A fast dependency injector for Android and Java.

Dagger A fast dependency injector for Java and Android. Dagger is a compile-time framework for dependency injection. It uses no reflection or runtime

Google 16.9k Jan 5, 2023
Fast Android Development. Easy maintainance.

Fast Android Development. Easy maintenance. AndroidAnnotations is an Open Source framework that speeds up Android development. It takes care of the pl

null 11.1k Dec 31, 2022
Fast Android Development. Easy maintainance.

Fast Android Development. Easy maintenance. AndroidAnnotations is an Open Source framework that speeds up Android development. It takes care of the pl

null 11.1k Dec 31, 2022
Bind Android views and callbacks to fields and methods.

Butter Knife Attention: This tool is now deprecated. Please switch to view binding. Existing versions will continue to work, obviously, but only criti

Jake Wharton 25.7k Jan 3, 2023
A fast dependency injector for Android and Java.

Dagger 1 A fast dependency injector for Android and Java. Deprecated – Please upgrade to Dagger 2 Square's Dagger 1.x is deprecated in favor of Google

Square 7.3k Jan 5, 2023
Google Guice on Android, version 3.0 [RETIRED]

As of August 2016, RoboGuice is no longer supported. For nearly 5 years it was the #1 dependency injection framework on Android due to its ease-of-use

null 3.8k Dec 26, 2022
Bind Android views and callbacks to fields and methods.

Butter Knife Attention: This tool is now deprecated. Please switch to view binding. Existing versions will continue to work, obviously, but only criti

Jake Wharton 25.7k Mar 22, 2021
lite your android ! the code is on the way~

android-lite-auto lite your android, the code is on the way~ ! LiteAuto是一个代码生成框架,思路参考 JakeWharton 的开源项目 ButterKnife,在它的思路基础添加了一些自己的想法,从0到1设计并实现的。 Lite

马天宇 32 Nov 2, 2022
Android SharedPreference management library made with kotlin

Kref Android SharedPreference management library made for kotlin Download Use Gradle: allprojects { repositories { ... maven { url

copin-danny 4 Mar 17, 2022
SharedPreference Library to save all types including your custom types and observe them if need be.

A SharedPreference Library that can be used to store all types including your custom classes and observe them too if needed.

Ehma Ugbogo 18 Nov 10, 2021
Easy SharedPreference Engine foR ANDROid

esperandro Easy SharedPreference Engine foR ANDROid What? esperandro makes it simple to use SharedPreferences in a nicer and less error-prone way. Wit

David Kunzler 184 Nov 25, 2022