All Projects β†’ skydoves β†’ Preferenceroom

skydoves / Preferenceroom

Licence: apache-2.0
🚚 Android processing library for managing SharedPreferences persistence efficiently and structurally.

Programming Languages

java
68154 projects - #9 most used programming language
kotlin
9241 projects

Projects that are alternatives of or similar to Preferenceroom

Kripton
A Java/Kotlin library for Android platform, to manage bean's persistence in SQLite, SharedPreferences, JSON, XML, Properties, Yaml, CBOR.
Stars: ✭ 110 (-67.74%)
Mutual labels:  annotation-processor, persistence, annotations, sharedpreferences
Hawk
βœ”οΈ Secure, simple key-value storage for Android
Stars: ✭ 3,827 (+1022.29%)
Mutual labels:  preferences, storage, sharedpreferences
Binaryprefs
Rapidly fast and lightweight re-implementation of SharedPreferences which stores each preference in files separately, performs disk operations via NIO with memory mapped byte buffers and works IPC (between processes). Written from scratch.
Stars: ✭ 484 (+41.94%)
Mutual labels:  preferences, storage, sharedpreferences
simple-preferences
Android Library to simplify SharedPreferences use with code generation.
Stars: ✭ 48 (-85.92%)
Mutual labels:  sharedpreferences, annotations, annotation-processor
Prefser
Wrapper for Android SharedPreferences with object serialization and RxJava Observables
Stars: ✭ 228 (-33.14%)
Mutual labels:  preferences, storage, sharedpreferences
Bulldog
Android library to simplify reading and writing to SharedPreferences, never write code like this anymore prefs.edit().putString("someKey","someString").apply()
Stars: ✭ 133 (-61%)
Mutual labels:  annotation-processor, preferences, sharedpreferences
memo
Android processing and secured library for managing SharedPreferences as key-value elements efficiently and structurally.
Stars: ✭ 18 (-94.72%)
Mutual labels:  sharedpreferences, storage, annotations
KVStorage
Android η»“ζž„εŒ–KVε­˜ε‚¨ζ‘†ζžΆοΌŒεŸΊδΊŽ yaml η”Ÿζˆ java η»“ζž„εŒ–ε­˜ε‚¨η±»
Stars: ✭ 228 (-33.14%)
Mutual labels:  sharedpreferences, storage
AnnotationProcessing
βœ”οΈγ…€[ARTICLE] Writing your own Annotation Processors in Android
Stars: ✭ 47 (-86.22%)
Mutual labels:  annotations, annotation-processor
ColdStorage
Lightweight data loading and caching library for android
Stars: ✭ 39 (-88.56%)
Mutual labels:  annotations, annotation-processor
GitHubSearch
GitHub iOS client with minimum third-party dependencies.
Stars: ✭ 34 (-90.03%)
Mutual labels:  dependency-injection, persistence
kesho
store cache any data type string, boolean, jsonObject, jsonArray, .....
Stars: ✭ 19 (-94.43%)
Mutual labels:  sharedpreferences, storage
zf-dependency-injection
Advanced dependency injection for laminas framework
Stars: ✭ 17 (-95.01%)
Mutual labels:  dependency-injection, annotations
AutoBindings
Set of annotations that aims to make your Android development experience easier along with lint checks.
Stars: ✭ 15 (-95.6%)
Mutual labels:  annotations, annotation-processor
WinAnalytics
A light-weight android library that can be quickly integrated into any app to use analytics tools.
Stars: ✭ 23 (-93.26%)
Mutual labels:  annotations, annotation-processor
AnnotationProcessorStarter
Project to set up basics of a Java annotation processor
Stars: ✭ 19 (-94.43%)
Mutual labels:  annotations, annotation-processor
apollo-cache-instorage
Apollo Cache implementation that facilitates locally storing resources
Stars: ✭ 98 (-71.26%)
Mutual labels:  storage, persistence
dagger2-ktx
Kotlin extension bridge library for Dagger2 (proof-of-concept)
Stars: ✭ 41 (-87.98%)
Mutual labels:  annotations, annotation-processor
nft.storage
πŸ˜‹ Free decentralized storage and bandwidth for NFTs on IPFS and Filecoin.
Stars: ✭ 309 (-9.38%)
Mutual labels:  storage, persistence
ToDoApp
πŸ“±My android playground app - Simple and Fastest todo app - developing to cover most android concepts, simple logic can make me focus more on framework
Stars: ✭ 28 (-91.79%)
Mutual labels:  sharedpreferences, dependency-injection

PreferenceRoom

License API Build Status Android Weekly
Android processor library for managing SharedPreferences persistence efficiently and structurally.
PreferenceRoom is inspired by Architecture Components Room Persistence and dagger.
PreferenceRoom integrates scattered SharedPreferences as an entity.
It supports setter & getter custom functions with security algorithm and could put & get objects. Also supports simple preference dependency injection with free from reflection. Fully supported in kotlin project.

What companies and projects are using this library?

GithubFollows
Open Source
All-In-One
Open Source
Battle Comics
Product
Epoptia
Open Source
Sensemore
Octocat allinone battleent epoptia Sensemore

Download

Maven Central

Gradle

Add below codes to your root build.gradle file (not your module build.gradle file).

allprojects {
    repositories {
        mavenCentral()
    }
}

And add a dependency code to your module's build.gradle file.

dependencies {
    implementation "com.github.skydoves:preferenceroom:1.2.1"
    annotationProcessor "com.github.skydoves:preferenceroom-processor:1.2.1"
    // in kotlin project use kapt instead of annotationProcessor
    kapt "com.github.skydoves:preferenceroom-processor:1.2.1"
}

Table of Contents

1.PreferenceEntity

2.PreferenceComponent

3.Dependency Injection (Use with dagger)

4.Usage in Kotlin (Incremental annotation processing)

5.Proguard-Rules

6.Debugging with Stetho

PreferenceEntity

preferenceentity
@PreferenceEntity annotation makes SharedPreferences data as an entity.
Value in @PreferenceEntity determines the entity name.
Entity's default name is determined by class name.

@PreferenceEntity("UserProfile")
public class Profile {
    protected final boolean login = false;
    @KeyName("nickname") protected final String userNickName = null;
    @KeyName("visits") protected final int visitCount = 1;

    @KeyName("userPet")
    @TypeConverter(PetConverter.class)
    protected Pet userPetInfo;

    @PreferenceFunction("nickname")
    public String putUserNickFunction(String nickname) {
        return "Hello, " + nickname;
    }

    @PreferenceFunction("nickname")
    public String getUserNickFunction(String nickname) {
        return nickname + "!!!";
    }

    @PreferenceFunction("visits")
    public int putVisitCountFunction(int count) {
        return ++count;
    }
}

After the build your project, Preference_(entity name) class will be generated automatically.

Preference_UserProfile userProfile = Preference_UserProfile.getInstance(this); // gets instance of the UserProfile entity.
userProfile.putNickname("my nickname"); // puts a value in NickName.
userProfile.getNickname(); // gets a nickname value.
userProfile.containsNickname(); // checks nickname key value is exist or not in entity.
userProfile.removeNickname(); // removes nickname key value in entity.
userProfile.nicknameKeyName(); // returns nickname key name.
userProfile.getEntityName(); // returns UserProfile entity name;
userProfile.getkeyNameList(); // returns UserProfile entity's key name lists.

// or invoke static.
Preference_UserProfile.getInstance(this).putNickname("my nickname");

we can listen the changed value using OnChangedListener.
onChanged method will be invoked if we change the value using put method.

userProfile.addNicknameOnChangedListener(new Preference_UserProfile.NicknameOnChangedListener() {
   @Override
   public void onChanged(String nickname) {
     Toast.makeText(getBaseContext(), "onChanged :" + nickname, Toast.LENGTH_SHORT).show();
   }
});

Auto-generated code is managed by singletons. But we can manage more efficiently using PreferenceComponent and Dependency Injection.

We can set SharedPreference to an entity as DefaultSharedPreferences using @DefaultPreference annotation like below.

@DefaultPreference
@PreferenceEntity("ProfileWithDefault")
public class UserProfilewithDefaultPreference {
    @KeyName("nickname")
    protected final String userNickName = "skydoves";
    
    // key name will be 'Login'. (login's camel uppercase)
    protected final boolean login = false;
}

The ProfileWithDefault entity from the example, will be initialized like below on PreferenceRoom processor.

PreferenceManager.getDefaultSharedPreferences(context);

So we can connect with PreferenceFragmentCompat, PreferenceScreen or etc.

KeyName

keyname
@KeyName annotation can be used on an entity class's field.
The field's key name will be decided by field name, but we can customize the name as taste.

@KeyName("visits") // keyname will be Visits.
protected final int visitCount = 1;

TypeConverter

typeconverter
SharedPreference persists only primitive type data.
But PreferenceRoom supports persistence obejct data using TypeConverter annotation.
@TypeConverter annotation should be annotated over an object field like below.

@TypeConverter(PetConverter.class)
protected Pet userPetInfo; // 'Pet' class field in an entity.

Below example is creating a converter class using Gson.
Converter class should extends the PreferenceTypeConverter<?> class.
The basic principle is making object class to string data for persistence and getting the string data and recover.
convertObject performs how to change class data to a string, convertType performs how to recover class data from the string data.

public class PetConverter extends PreferenceTypeConverter<Pet> {

    private final Gson gson;

    // default constructor will be called by PreferenceRoom
    public PetConverter() {
        this.gson = new Gson();
    }

    @Override
    public String convertObject(Pet pet) {
        return gson.toJson(pet);
    }

    @Override
    public Pet convertType(String string) {
        return gson.fromJson(string, Pet.class);
    }
}

BaseTypeConverter

We can generalize the converter using generic like below.

public class BaseGsonConverter<T> extends PreferenceTypeConverter<T> {

    private final Gson gson;

    public BaseGsonConverter(Class<T> clazz) {
        super(clazz);
        this.gson = new Gson();
    }

    @Override
    public String convertObject(T object) {
        return gson.toJson(object);
    }

    @Override
    public T convertType(String string) {
        return gson.fromJson(string, clazz);
    }
}

So we can use the converter to any classes.

@KeyName("userinfo")
@TypeConverter(BaseGsonConverter.class)
protected PrivateInfo privateInfo;

@KeyName("userPet")
@TypeConverter(BaseGsonConverter.class)
protected Pet userPetInfo;

PreferenceFunction

preferencefunction
@PreferenceFunction annotation processes pre/post-processing through getter and setter functions.
@PreferenceFunction annotation's value decides a target. The target should be a keyName.
The function's naming convention is which should start with put or get prefix.
put_functionname_ is pre-processing getter function and get_functionname_ is postprocessing getter function.

@PreferenceFunction("nickname")
public String putUserNickFunction(String nickname) {
    return "Hello, " + nickname;
}

@PreferenceFunction("nickname")
public String getUserNickFunction(String nickname) {
    return nickname + "!!!";
}

EncryptEntity

SharedPreferences data are not safe from hacking even if private-mode.
There is a simple way to encrypt whole entity using @EncryptEntity annotation.
It is based on AES128 encryption. So we should set the key value along 16 size length.
If put method invoked, the value is encrypted automatically.
And if get method invoked, it returns the decrypted value.

@EncryptEntity("1234567890ABCDFG")
@PreferenceEntity("UserProfile")
public class Profile {
}

Or we can customize encrypting and decrypting algorithm using PreferenceFunction.

@PreferenceFunction("uuid")
public String putUuidFunction(String uuid) {
   return SecurityUtils.encrypt(uuid);
}

@PreferenceFunction("uuid")
public String getUuidFunction(String uuid) {
    return SecurityUtils.decrypt(uuid);
}

PreferenceComponent

preferencecomponent
PreferenceComponent integrates entities. @PreferenceComponent annotation should be annotated on an interface class.
We can integrate many entities as one component using entities value in @PreferenceComponent annotation.
So we can initialize many entities at once through the component and get instances from the component.
And the instance of the PreferenceComponent is managed by singleton by PreferenceRoom. `PreferenceComponent's instance also singletons. And all entities instances are initialized when the component is initialized.

@PreferenceComponent(entities = {Profile.class, Device.class})
public interface UserProfileComponent {
}

After build your project, PreferenceComponent_(component's name) class will be generated automatically.
The best-recommanded way to initialize component is initializing on Application class. So we can manage the component as a singleton instance.

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        PreferenceComponent_UserProfileComponent.init(this);
    }
}

After initializing the component, we can access and use the instances of component and component's entities anywhere.

PreferenceComponent_UserProfileComponent component = PreferenceComponent_UserProfileComponent.getInstance();
Preference_UserProfile userProfile = component.UserProfile();
Preference_UserDevice userDevice = PreferenceComponent_UserProfileComponent.getInstance().UserDevice();

Dependency Injection

di
PreferenceRoom supports simple dependency injection process with free from reflection using @InjectPreference annotation. But If you want to use with dagger, check this reference.

Firstly we should declare some target classes which to be injected preference instances in PreferenceComponent.

@PreferenceComponent(entities = {Profile.class, Device.class})
public interface UserProfileComponent {
    // declares targets for the dependency injection.
    void inject(MainActivity __);
    void inject(LoginActivity __);
}

We should annotate InjectPreference annotation to preference entity or component class field which generated by PreferenceRoom.
The field's modifier should be public.

@InjectPreference
public PreferenceComponent_UserProfileComponent component;

@InjectPreference
public Preference_UserProfile userProfile;

And the last, we should inject instances of entity and component to targets fields using inject method from an instance of the component.

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   PreferenceComponent_UserProfileComponent.getInstance().inject(this);
}

Usage in Kotlin

It is similar to java project usage.
But the most important thing is which we should use an open modifier on entity classes and PreferenceFunctions.
And the field's modifier should be @JvmField val.

@PreferenceEntity("UserDevice")
open class Device {
    @KeyName("version")
    @JvmField val deviceVersion: String? = null

    @KeyName("uuid")
    @JvmField val userUUID: String? = null

    @PreferenceFunction("uuid")
    open fun putUuidFunction(uuid: String?): String? {
        return SecurityUtils.encrypt(uuid)
    }

    @PreferenceFunction( "uuid")
    open fun getUuidFunction(uuid: String?): String? {
        return SecurityUtils.decrypt(uuid)
    }
}

And the component usage is almost the same as Java examples.

@PreferenceComponent(entities = [Profile::class, Device::class])
interface UserProfileComponent {
    // declare dependency injection targets.
    fun inject(target: MainActivity)
    fun inject(target: LoginActivity)
}

And the last, the usage of the dependency injection is the same as the java. but we should declare the component and entity field's modifier as lateinit var.

@InjectPreference
lateinit var component: PreferenceComponent_UserProfileComponent
    
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
    PreferenceComponent_UserProfileComponent.getInstance().inject(this) // inject dependency injection to MainActivity.

Incremetal annotation processing

Starting from version 1.3.30, kapt supports incremental annotation processing as an experimental feature. Currently, annotation processing can be incremental only if all annotation processors being used are incremental.
Incremental annotation processing is enabled by default starting from version 1.3.50.
PreferenceRoom supports incremental annotation processing since version 1.1.9 and here is a Before/After example result of the enabled.

Before (23.758s) After (18.779s)

Non Existent Type Correction

If you encounter NonExistentClass error at compile time, you should add below codes on your build.gradle.
Default, Kapt replaces every unknown type (including types for the generated classes) to NonExistentClass, but you can change this behavior. Add the additional flag to the build.gradle file to enable error type inferring in stubs:

kapt {
  correctErrorTypes = true
}

Proguard Rules

# Retain generated class which implement PreferenceRoomImpl.
-keep public class ** implements com.skydoves.preferenceroom.PreferenceRoomImpl

# Prevent obfuscation of types which use PreferenceRoom annotations since the simple name
# is used to reflectively look up the generated Injector.
-keep class com.skydoves.preferenceroom.*
-keepclasseswithmembernames class * { @com.skydoves.preferenceroom.* <methods>; }
-keepclasseswithmembernames class * { @com.skydoves.preferenceroom.* <fields>; }

Debugging with Stetho

We can debug SharedPreferences values which managed by PreferenceRoom using Stetho.
screenshot635705571

References

Find this library useful? ❀️

Support it by joining stargazers for this repository. ⭐️

Sponsor β˜•οΈ

If you feel like to sponsor me a coffee for my efforts, I would greatly appreciate it.

Buy Me A Coffee

License

Copyright 2017 skydoves

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.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].