Very easy to use wrapper library for Android SharePreferences

Related tags

Utility Treasure
Overview

Treasure

Travis CI Download Android Arsenal

English document

Treasure是一个Android平台上基于SharePreferences的偏好存储库,只需要定义接口,无需编写实现,默认支持SerializableParcelable。运行时0反射,不仅使用方便而且性能和原生写法几乎无差别。

使用方法

1、添加依赖

Gradle

compile 'com.baoyz.treasure:treasure:0.7.4'
annotationProcessor 'com.baoyz.treasure:treasure-compiler:0.7.4'

Gradle Plugin

如果在多 Module 中同时使用 Treasure,会出现 Multiple dex files define Lcom/baoyz/treasure/PreferencesFinder 错误,使用 Treasure Gradle Plugin 可以解决这个错误。

buildscript 中添加依赖

dependencies {
    classpath 'com.baoyz.treasure:treasure-gradle:0.7.4'
}

然后只在你的 Application Module 中 apply 插件,Library Module 不需要。

apply plugin: 'com.baoyz.treasure'

如果没有多 Module 同时使用 Treasure 的情况,不需要使用这个插件。

2、定义接口
@Preferences
public interface SimplePreferences {

    String getUsername();

    void setUsername(String username);

}

我们定义了一个interface,需要使用@Preferences注解进行声明。然后可以定义一系列的getset方法,用于获取和设置值。方法名会作为存储的key,例如getUsername()setUsername()key就是username,也就是通过setUsername()设置的value可以通过getUsername()获取到,因为他们的key是一样的。

3、实例化
SimplePreferences preferences = Treasure.get(context, SimplePreferences.class);
preferences.setUsername("Hello Treasure!");
preferences.getUsername(); // return "Hello Treasure!"

通过Treasure.get()方法可以获取指定的Preferences对象,之后可以调用set方法设置值,通过对应的get方法获取值。

高级用法

多文件

可以为一个Preferences生成多个文件,例如多账号管理,不同账号有不同Preferences

Treasure.get(context, SimplePreferences.class, "id_one");
Treasure.get(context, SimplePreferences.class, "id_two");

Treasure提供了一个重载的get方法,可以传入一个String类型的ID,不同ID返回的Preferences对象不同,保存的文件也不同。(感谢好基友zzz40500提出的建议)

默认值

@Default注解可以指定get方法的默认值。

@Default("Hello Treasure!")
String getUsername();

@Default("false")
boolean isLogin();

// default is 1 hour
@Default("1000 * 60 * 60")
long getTimeout();

@Default({"hello", "world", "!"})
Set<String> getStringSet();

如果没有指定@Default那么默认值见下表。

返回值类型 默认值
int 0
float 0f
long 0l
boolean false
String null
Set<String> null

提交类型

设置值的默认提交类型是edit().apply(),如果想使用edit().commit()有三种方式。

1、全局
@Preferences(edit = Preferences.Edit.COMMIT)
public interface SimplePreferences

全局指定提交方式之后,所有的set方法都会以commit()方法提交数据。

2、注解指定方法
@Commit
void setUsername(String username);

使用@Commit注解只对当前方法有效。

3、指定方法返回boolean
boolean setUsername(String username);

无论上面两种方式有没有设置,只要set方法的返回值是boolean,那么这个方法就会以commit()方法提交,并且返回commit()的结果。

移除数据

@Remove
void removeUsername();

@Remove
void deleteTimeout();

使用@Remove注解修饰方法,调用方法移除对应key的数据。

清空数据

@Clear
void clear();

可以声明一个方法,使用@Clear注解修饰,那么调用这个方法就会清空整个Preferences的数据。

有效期

可以使用@Expired指定某个配置的有效期。开始时间是最后一次set的时间。@Expired可以修饰在gettersetter方法 ,效果相同。

// 10秒钟后过期
@Expired(value = 10, unit = Expired.UNIT_SECONDS)
String getTestExpired();

@Expired还可以修饰在方法参数上,用于动态指定有效时间,只能在setter方法的参数上修饰。

// second参数的值是有效时间,单位是UNIT_MINUTES(分钟)
void setTestExpired(String value, @Expired(unit = Expired.UNIT_MINUTES) int second);

@Expiredunit不指定默认是UNIT_MILLISECONDS毫秒。

对象序列化

如果interface中声明的数据类型不是SharePreferences支持的,需要用到转换器,Treasure默认提供SerializableParcelable的支持。

// Serializable or Parcelable
class User implements Serializable {...}

// Preferences Interface
void setUser(User user);
User getUser();

可以自定义转换规则,例如用Gson将对象以JSON的形式保存。

public class GsonConverterFactory implements Converter.Factory {

    @Override
    public <F> Converter<F, String> fromType(Type fromType) {
        return new Converter<F, String>() {
            @Override
            public String convert(F value) {
                return new Gson().toJson(value);
            }
        };
    }

    @Override
    public <T> Converter<String, T> toType(final Type toType) {
        return new Converter<String, T>() {
            @Override
            public T convert(String value) {
                return new Gson().fromJson(value, toType);
            }
        };
    }
}

自定义之后,需要调用Treasure.setConverterFactory()方法设置自定义的转换规则。

Treasure.setConverterFactory(new GsonConverterFactory());

获取 SharedPreferences 对象

@Prototype
SharedPreferences getSharedPreferences();

使用 @Prototype 修饰,返回值是 SharedPreferences 类型,调用此方法可以获取原始的 SharedPreferences 对象。

自定义 Key

@Key("custom_key")
String getValue();
@Key("custom_key")
void setValue(String value);

关于方法名

如果方法名以getsetputisremovedelete开头,那么会忽略这些前缀并且全部小写作为key,如果不包含这些前缀,那么方法名全部小写会作为key

Proguard

Treasure运行时0反射,不需要添加Proguard配置。

致谢

Favor(灵感来源)

javapoet

License

The MIT License (MIT)

Copyright (c) 2015 baoyongzhang <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Comments
  • 编译问题

    编译问题

    请问是用AndroidStudio2.0可以编译你的工程?我导入一直编译不到! 1.javax.annotation.processing.FilerException: Attempt to recreate a file for type com.baoyz.treasure.PreferencesFinder

    2.注: D:\AndroidStudioProjects\Treasure-master\demo\build\intermediates\classes\debug\com\baoyz\treasure\PreferencesFinder.java使用了未经检查或不安全的操作。

    3.java.lang.RuntimeException: Exception parsing classes at com.android.dx.command.dexer.Main.processClass(Main.java:752) at com.android.dx.command.dexer.Main.processFileBytes(Main.java:718) at com.android.dx.command.dexer.Main.access$1200(Main.java:85) at com.android.dx.command.dexer.Main$FileBytesConsumer.processFileBytes(Main.java:1645) at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284) at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166) at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144) at com.android.dx.command.dexer.Main.processOne(Main.java:672) at com.android.dx.command.dexer.Main.processAllFiles(Main.java:574) at com.android.dx.command.dexer.Main.runMonoDex(Main.java:311) at com.android.dx.command.dexer.Main.run(Main.java:277) at com.android.dx.command.dexer.Main.main(Main.java:245) at com.android.dx.command.Main.main(Main.java:106) Caused by: com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000) at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472) at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406) at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388) at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251) at com.android.dx.command.dexer.Main.parseClass(Main.java:764) at com.android.dx.command.dexer.Main.access$1500(Main.java:85) at com.android.dx.command.dexer.Main$ClassParserTask.call(Main.java:1684) at com.android.dx.command.dexer.Main.processClass(Main.java:749) ... 12 more 1 error; aborting Error:Execution failed for task ':demo:preDexDebug'.

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

    opened by darin-lao 5
  • Treasure.get() return null

    Treasure.get() return null

    你好,我很喜欢你的这个库。 不过今天尝试使用的时候发现Treasure.get() 总是return null,所以进一步进行get 和set methods的时候就出现NPE。

    我的代码非常简单,就如同例子一样

    public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        final SimplePreferences preferences = Treasure.get(this, SimplePreferences.class);
        preferences.setUsername("Hello Treasure!");
        String username = preferences.getUsername(); // "Hello Treasure!"}
    

    @Preferences public interface SimplePreferences {

    @Default("Hello Treasure!")
    String getUsername();
    
    @Commit
    void setUsername(String username);}
    

    不过final SimplePreferences preferences = Treasure.get(this, SimplePreferences.class); 这句一直都是null。 请问你了解可能产生这个问题的原因吗? 有可能是annotationprocessor的设置问题吗? 谢谢

    opened by ben-zhong 4
  • Custom key support

    Custom key support

    As far as i understand custom key annotation is not supported now. It would be very convenient to have that feature while migrating to treasure library in existing projects. Usually people already created some preferences and it is not convenient to write interface methods names so that they match key names (for example If i have pref key "key_is_first_launch" i have to write getter method signature as "boolean isKey_Is_First_Launch()"). Do you plan to make such feature or i can pull-request it?

    enhancement 
    opened by xgear-public 1
  • 支持有效期配置@Expired()

    支持有效期配置@Expired()

    有时候需要有对kev-vavel有一定的时效性: e.g

    // 默认一小时后过期
    @Expired(duration=1000 * 60 * 60)
    @Default("root")
    String getUsername();
    

    Inteface.java

    setUsername(username) // 默认不过期
    setUsername(username, expired) //optional,设置过期时间
    
    opened by Jayin 1
  • Failed resolution of: Lcom/baoyz/treasure/PreferencesFinder

    Failed resolution of: Lcom/baoyz/treasure/PreferencesFinder

    at com.baoyz.treasure.Treasure.get(Treasure.java:61) at com.baoyz.treasure.Treasure.get(Treasure.java:50)

    Didn't find class "com.baoyz.treasure.PreferencesFinder" on path: DexPathList[[zip file "/data/app/cn.test-1/base.apk"],nativeLibraryDirectories=[/data/app/cn.test-1/lib/arm64, /system/lib64, /vendor/lib64]]

    annotationProcessor 'com.baoyz.treasure:treasure-compiler:0.7.4' 修改成 provided 'com.baoyz.treasure:treasure-compiler:0.7.4'

    正常运行

    ??

    opened by DizzyDuan 0
  • 作者还更新么?多个module使用出粗错了

    作者还更新么?多个module使用出粗错了

    说是无法找到’com.baoyz.treasure.Converter$Factory‘

    Task :app:transformClassesWithMergePreferencesFinderForAibaoDebug javassist.CannotCompileException: [source error] javassist.NotFoundException: com.baoyz.treasure.Converter$Factory at javassist.CtBehavior.setBody(CtBehavior.java:446) at javassist.CtBehavior.setBody(CtBehavior.java:412) at javassist.CtBehavior$setBody.call(Unknown Source) at com.baoyz.treasure.MergeFinderTransform$_transform_closure1$_closure5.doCall(MergeFinderTransform.groovy:154) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:264) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034) at groovy.lang.Closure.call(Closure.java:418) at groovy.lang.Closure.call(Closure.java:434) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2125) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2110) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2151) at org.codehaus.groovy.runtime.dgm$163.invoke(Unknown Source) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128) at com.baoyz.treasure.MergeFinderTransform$_transform_closure1.doCall(MergeFinderTransform.groovy:101) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:264) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034) at groovy.lang.Closure.call(Closure.java:418) at groovy.lang.Closure.call(Closure.java:434) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2125) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2110) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2151) at org.codehaus.groovy.runtime.dgm$163.invoke(Unknown Source) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128) at com.baoyz.treasure.MergeFinderTransform.transform(MergeFinderTransform.groovy:73) at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:221) at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:217) at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102) at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:212) at sun.reflect.GeneratedMethodAccessor2701.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73) at org.gradle.api.internal.project.taskfactory.IncrementalTaskAction.doExecute(IncrementalTaskAction.java:50) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:131) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:120) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:99) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:77) at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51) at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59) at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54) at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59) at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101) at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44) at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91) at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62) at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59) at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54) at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43) at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.run(EventFiringTaskExecuter.java:51) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:46) at org.gradle.execution.taskgraph.LocalTaskInfoExecutor.execute(LocalTaskInfoExecutor.java:42) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:277) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:262) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:135) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:130) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.execute(DefaultTaskPlanExecutor.java:200) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.executeWithWork(DefaultTaskPlanExecutor.java:191) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.run(DefaultTaskPlanExecutor.java:130) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55) at java.lang.Thread.run(Thread.java:748)

    opened by bailibaili 0
  • 支持java8的default方法

    支持java8的default方法

    boolean isFirstIn();
    
    boolean isFromUpdate();
    
    // 默认方法
    default boolean needShowHint() {
        return isFirstIn() && isFromUpdate();
    }
    
    // 静态调用
    static AppPreference get(Context context) {
        return Treasure.get(context, AppPreference.class, UserInfoManager.userId);
    }
    

    这种方式是否支持,对于前缀是否有强制要求。

    感觉可以在注解层面对于没有实现的方法进行实现,如果有了默认实现,那么则忽略。

    help wanted 
    opened by tianzhijiexian 0
  • 在库中使用Treasure

    在库中使用Treasure

    你好,我最近在尝试使用Treasure。

    我在项目的其中一个module里引用了Treasure,使用的时候并没有问题(因为app和其他module本身并没有使用Treasure,所以就没有使用gradle plugin)。

    但当我把那个moudle分离出来,单独打包成一个aar时,再导入原项目后,运行的时候就出现了如下错误:

    java.lang.NoClassDefFoundError: Failed resolution of: Lcom/baoyz/treasure/Treasure; Caused by: java.lang.ClassNotFoundException: Didn't find class "com.baoyz.treasure.Treasure" on path: DexPathList[...]

    请问你知道原因吗,谢谢

    opened by ben-zhong 4
Owner
星一
Read the fucking code
星一
nestegg - Very simple Kotlin caching library

nestegg - Very simple Kotlin caching library

Runner-be 4 Jun 15, 2022
🪁 Android Resources Wrapper Library

Kite Android Resource Wrapper Library. TLDR Fed up with typing ContextCompat, resources and context all over your apps to access your resources? Say n

Andrea Cioccarelli 132 Dec 9, 2022
A simple and easy to use stopwatch and timer library for android

TimeIt Now with Timer support! A simple and easy to use stopwatch and timer library for android Introduction A stopwatch can be a very important widge

Yashovardhan Dhanania 35 Dec 10, 2022
Android Shared preference wrapper than encrypts the values of Shared Preferences. It's not bullet proof security but rather a quick win for incrementally making your android app more secure.

Secure-preferences - Deprecated Please use EncryptedSharedPreferences from androidx.security in preferenced to secure-preference. (There are no active

Scott Alexander-Bown 1.5k Dec 24, 2022
Wrapper around the android Camera class that simplifies its usage

EasyCamera Wrapper around the android Camera class that simplifies its usage (read more about the process) Usage: // the surface where the preview wil

Bozhidar Bozhanov 641 Dec 29, 2022
React Native wrapper to bridge our iOS and Android SDK

React Native wrapper to bridge our iOS and Android SDK

Intercom 94 Jan 1, 2023
An easy-to-use, cross-platform measurement tool that pulls data out of CD pipelines and analysis the four key metrics for you.

Maintained by SEA team, ThoughtWorks Inc. Read this in other languages: English, 简体中文 Table of Contents About the Project Usage How to Compute Contrib

Thoughtworks 277 Jan 7, 2023
Android library which makes it easy to handle the different obstacles while calling an API (Web Service) in Android App.

API Calling Flow API Calling Flow is a Android library which can help you to simplify handling different conditions while calling an API (Web Service)

Rohit Surwase 19 Nov 9, 2021
:iphone: [Android Library] Get device information in a super easy way.

EasyDeviceInfo Android library to get device information in a super easy way. The library is built for simplicity and approachability. It not only eli

Nishant Srivastava 1.7k Dec 22, 2022
Compose easy forms validation library

Compose EasyForms Focus on building your form UI while the library do the heavy work for you. Features Built in support for most of the Form widgets i

Kosh Sergani 24 Jul 18, 2022
[] Easy async loading for Android's ListView/GridView

NOTE: Smoothie's API is not final yet. Although the library is fairly funcional, this is still beta-quality code. Do not rely on it for production cod

Lucas Rocha 988 Dec 22, 2022
Access and process various types of personal data in Android with a set of easy, uniform, and privacy-friendly APIs.

PrivacyStreams PrivacyStreams is an Android library for easy and privacy-friendly personal data access and processing. It offers a functional programm

null 269 Dec 1, 2022
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
It's finally easy to take photos/videos via camera or get photos/videos from gallery on Android.

Shutter-Android It's finally easy to take photos/videos via camera or get photos/videos from gallery on Android. What is Shutter? Shutter is an Androi

Levi Bostian 56 Oct 3, 2022
Interactive prompts made easy!

Interactive prompts made easy!

David Simon 1 Nov 1, 2021
This is a easy way to publish MQTT message and receive MQTT message

SMQ-CLIENT This is a easy way to publish MQTT message and receive MQTT message This is provider a spring stater for quick use Recive message form the

SUDA 1 Apr 25, 2022
Use Android as Rubber Ducky against another Android device

Use Android as Rubber Ducky against another Android device

null 1.4k Jan 9, 2023
Multi-module, Kotlin, MVI, Compose, Hilt, Navigation Component, Use-cases, Room, Retrofit

Work in progress Multi-module demo app that gets data from dota2 api. API https://docs.opendota.com/ Players by rank (GET) https://api.opendota.com/ap

Mitch Tabian 321 Dec 27, 2022
Android Utilities Library build in kotlin Provide user 100 of pre defined method to create advanced native android app.

Android Utilities Library build in kotlin Provide user 100 of pre defined method to create advanced native android app.

Shahid Iqbal 4 Nov 29, 2022