2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jul 22 12:37:26 IRDT 2021
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-7.0.2-bin.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/IORun.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 |
6 |
7 | fun IORun(run:()->T,cb:(v:T)->Unit){
8 | Thread{
9 | val result = run()
10 | Handler(Looper.getMainLooper()).post { cb(result) }
11 | }.start()
12 | }
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Thu Jul 22 12:27:31 IRDT 2021
8 | sdk.dir=/home/Ali/Android/sdk
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/security/DeviceKeyGenerator.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.security
2 |
3 | import android.content.Context
4 | import android.provider.Settings.Secure
5 |
6 | /**
7 | * Created by ali on 8/30/18.
8 | */
9 | object DeviceKeyGenerator {
10 | fun Generate(context: Context): String {
11 | return Secure.getString(context.contentResolver, Secure.ANDROID_ID)
12 | }
13 | //recommended
14 | fun Generate(context: Context, secret: String): String {
15 | val id = Secure.getString(context.contentResolver, Secure.ANDROID_ID)
16 | return id.substring(secret.length) + secret
17 | }
18 | }
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__com_google_code_gson_gson_2_8_6.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 30
7 | defaultConfig {
8 | applicationId "com.example.ali.easyrepo"
9 | minSdkVersion 15
10 | targetSdkVersion 30
11 | versionCode 1
12 | versionName "1.0"
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 |
23 | dependencies {
24 | implementation project(':easydata')
25 | implementation 'com.google.code.gson:gson:2.8.6'
26 | }
27 |
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/easydata/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 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_72.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/repos/SafeBox.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.repos
2 |
3 | import android.content.Context
4 | import com.ali77gh.easydata.security.Encryption
5 | import com.ali77gh.easydata.security.Encryption.generateKey
6 | import javax.crypto.SecretKey
7 |
8 | /**
9 | * Created by ali on 8/30/18.
10 | */
11 | class SafeBox(context: Context, key: String) {
12 |
13 | private var secretKey: SecretKey = generateKey(key)
14 | private val byteRepo: ByteDAO = ByteDAO(context, RootMode.LOCAL)
15 |
16 | fun save(fileName: String, sensitiveData: String) =
17 | byteRepo.save(
18 | fileName,
19 | Encryption.encrypt(sensitiveData, secretKey)
20 | )
21 |
22 | fun load(fileName: String) =
23 | Encryption.decrypt(
24 | byteRepo.load(fileName),
25 | secretKey
26 | )
27 | }
--------------------------------------------------------------------------------
/EasyRepo.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_72.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/EasyDataAndroid.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
19 |
20 |
21 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/repos/GRepo.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.repos
2 |
3 | import android.content.Context
4 | import android.os.Environment
5 | import java.io.File
6 |
7 | /**
8 | * Created by ali on 8/20/18.
9 | */
10 |
11 | /**
12 | * this needs permission and runtime permission
13 | */
14 | enum class RootMode {
15 | LOCAL,
16 | CACHE,
17 | EXTERNAL
18 | }
19 |
20 | abstract class GRepo(context: Context, mode: RootMode) {
21 |
22 | protected var root: File = when (mode) {
23 | RootMode.LOCAL -> context.filesDir
24 | RootMode.CACHE -> context.cacheDir
25 | RootMode.EXTERNAL -> Environment.getExternalStorageDirectory()
26 | }
27 |
28 | fun checkExist(fileName: String) = File("$root/$fileName").exists()
29 |
30 | /**
31 | * @param filename be careful about user files in external storage mode
32 | */
33 | fun remove(filename: String) {
34 | val f = File("$root/$filename")
35 | if (f.exists()) f.delete()
36 | }
37 | }
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_android_extensions_runtime_1_3_72.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 ali ghahremani
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 |
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/security/Encryption.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.security
2 |
3 | import java.lang.RuntimeException
4 | import javax.crypto.*
5 | import javax.crypto.spec.SecretKeySpec
6 |
7 | /**
8 | * Created by ali on 8/12/18.
9 | */
10 | object Encryption {
11 |
12 | //key should be 16 or 32 bytes
13 | fun generateKey(key: String): SecretKey {
14 | return when(key.length){
15 | 0->throw RuntimeException("empty key")
16 | 32 -> SecretKeySpec(key.toByteArray(), "AES")
17 | in 1..32 -> generateKey(key + key)
18 | else -> generateKey(key.substring(0,32))
19 | }
20 | }
21 |
22 | fun encrypt(message: String, secret: SecretKey): ByteArray {
23 | val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
24 | cipher.init(Cipher.ENCRYPT_MODE, secret)
25 | return cipher.doFinal(message.toByteArray())
26 | }
27 |
28 | fun decrypt(cipherText: ByteArray?, secret: SecretKey): String {
29 | val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
30 | cipher.init(Cipher.DECRYPT_MODE, secret)
31 | return String(cipher.doFinal(cipherText))
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ali77gh/easydataexample/Settings.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydataexample
2 |
3 | import android.content.Context
4 | import com.ali77gh.easydata.repos.StringDAO
5 |
6 | class Settings private constructor(context: Context) {
7 |
8 | private val repo = StringDAO(context)
9 |
10 | var theme
11 | get() = repo.load("theme","dark")
12 | set(value) = repo.save("theme",value)
13 |
14 | var notification : Boolean
15 | get() = repo.load("notification","true").toBoolean()
16 | set(value) = repo.save("notification",value.toString())
17 |
18 | var lastLogin : Long
19 | get() = repo.load("lastLogin","0").toLong()
20 | set(value) = repo.save("lastLogin",value.toString())
21 |
22 | enum class DateSystem{Jalali,Hijri,Gregorian}
23 | var dateSystem : DateSystem
24 | get() = DateSystem.valueOf(repo.load("dateSystem", DateSystem.Jalali.name))
25 | set(value) = repo.save("dateSystem",value.name)
26 |
27 | companion object{
28 | var settings: Settings?= null
29 | fun get(context: Context): Settings {
30 | if (settings ==null) settings = Settings(context)
31 | return settings!!
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/repos/ObjectDAO.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.repos
2 |
3 | import android.content.Context
4 | import android.os.Handler
5 | import android.os.Looper
6 | import com.google.gson.Gson
7 |
8 | /**
9 | * Created by ali on 8/22/18.
10 | */
11 | class ObjectDAO(
12 | context: Context,
13 | mode: RootMode
14 | ) : GRepo(context,mode) {
15 |
16 | private val gson = Gson()
17 | private val stringRepo = StringDAO(context,mode)
18 |
19 | fun load(filename: String, type: Class<*>) =
20 | gson.fromJson(
21 | stringRepo.load(filename),
22 | type
23 | )
24 |
25 | fun loadAsync(filename: String, type: Class<*>, callback: (data:Any)->Unit) {
26 | Thread {
27 | val obj = load(filename, type)
28 | Handler(Looper.getMainLooper()).post { callback(obj) }
29 | }.start()
30 | }
31 |
32 | fun save(filename: String, obj: Any) =
33 | stringRepo.save(filename, gson.toJson(obj))
34 |
35 | fun saveAsync(filename: String, obj: Any, callback: ()->Unit={}) {
36 | Thread {
37 | save(filename, obj)
38 | Handler(Looper.getMainLooper()).post { callback() }
39 | }.start()
40 | }
41 | }
--------------------------------------------------------------------------------
/easydata/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'maven-publish'
5 |
6 | android {
7 | compileSdkVersion 30
8 | defaultConfig {
9 | minSdkVersion 15
10 | targetSdkVersion 30
11 | versionName '3.2.0'
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | versionNameSuffix '3.2.0'
14 | versionCode 4
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | productFlavors {
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 | }
29 |
30 | dependencies {
31 | implementation 'com.google.code.gson:gson:2.8.6'
32 | }
33 | afterEvaluate {
34 | publishing {
35 | publications {
36 | release(MavenPublication) {
37 |
38 | from components.release
39 |
40 | groupId = 'com.ali77gh'
41 | artifactId = 'easydata'
42 | version = '3.2'
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/repos/ByteDAO.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.repos
2 |
3 | import android.content.Context
4 | import android.os.Handler
5 | import android.os.Looper
6 | import java.io.*
7 |
8 | /**
9 | * Created by ali on 8/20/18.
10 | */
11 | class ByteDAO(
12 | context: Context,
13 | mode: RootMode
14 | ) : GRepo(context, mode) {
15 |
16 | fun load(filename: String): ByteArray? {
17 | val path = "$root/$filename"
18 | val size = File(path).length().toInt()
19 | val bytes = ByteArray(size)
20 | try {
21 | val buf = BufferedInputStream(FileInputStream(path))
22 | buf.read(bytes, 0, bytes.size)
23 | buf.close()
24 | return bytes
25 | } catch (e: IOException) {
26 | e.printStackTrace()
27 | }
28 | return null
29 | }
30 |
31 | fun loadAsync(filename: String, callback: (bytes:ByteArray?)->Unit) {
32 | Thread {
33 | val bytes = load(filename)
34 | Handler(Looper.getMainLooper()).post { callback(bytes) }
35 | }.start()
36 | }
37 |
38 | fun save(filename: String, bytes: ByteArray) {
39 | val path = "$root/$filename"
40 | val out = FileOutputStream(path)
41 | out.write(bytes)
42 | out.flush()
43 | out.close()
44 | }
45 |
46 | fun saveAsync(filename: String, bytes: ByteArray, callback: ()->Unit={}) {
47 | Thread {
48 | save(filename, bytes)
49 | Handler(Looper.getMainLooper()).post { callback() }
50 | }.start()
51 | }
52 | }
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/repos/StringDAO.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.repos
2 |
3 | import android.content.Context
4 | import android.os.Handler
5 | import android.os.Looper
6 | import android.util.Log
7 | import java.io.*
8 |
9 | /**
10 | * Created by ali on 8/22/18.
11 | */
12 | class StringDAO(
13 | context: Context,
14 | rootMode: RootMode = RootMode.LOCAL
15 | ) : GRepo(context,rootMode) {
16 |
17 | /**
18 | * @param default default value of default is ""
19 | * @return default if not exist
20 | */
21 | fun load(fileName: String,default:String=""): String {
22 | if (!checkExist(fileName)) return default
23 |
24 | val path = "$root/$fileName"
25 |
26 | val inputStream = FileInputStream(File(path))
27 | val inputStreamReader = InputStreamReader(inputStream)
28 | val bufferedReader = BufferedReader(inputStreamReader)
29 | var receiveString: String?
30 | val stringBuilder = StringBuilder()
31 | while (bufferedReader.readLine().also { receiveString = it } != null) {
32 | stringBuilder.append(receiveString)
33 | }
34 | inputStream.close()
35 | return stringBuilder.toString()
36 | }
37 |
38 | fun loadAsync(filename: String, callback: (data:String)->Unit) {
39 | Thread {
40 | val data = load(filename)
41 | Handler(Looper.getMainLooper()).post { callback(data) }
42 | }.start()
43 | }
44 |
45 | fun save(fileName: String, data: String) {
46 | val path = "$root/$fileName"
47 | Log.d("yohogooo_path",path)
48 | val outputStreamWriter = OutputStreamWriter(FileOutputStream(File(path)))
49 | outputStreamWriter.write(data)
50 | outputStreamWriter.close()
51 | }
52 |
53 | fun saveAsync(filename: String, data: String, callback: ()->Unit={}) {
54 | Thread {
55 | save(filename,data)
56 | Handler(Looper.getMainLooper()).post { callback() }
57 | }.start()
58 | }
59 | }
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/repos/BitmapDAO.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.repos
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import android.graphics.BitmapFactory
6 | import android.os.Handler
7 | import android.os.Looper
8 | import java.io.File
9 | import java.io.FileOutputStream
10 |
11 | /**
12 | * Created by ali on 8/20/18.
13 | */
14 | class BitmapDAO(
15 | context: Context,
16 | mode: RootMode
17 | ) : GRepo(context, mode) {
18 |
19 | @SuppressWarnings("loading bitmap in main thread not recommended (use loadSync)")
20 | fun load(fileName: String): Bitmap? {
21 | val path = "$root/$fileName"
22 | val f = File(path)
23 | return if (f.exists())
24 | BitmapFactory.decodeFile(path)
25 | else null
26 | }
27 |
28 | fun loadAsync(filename: String, callback: (bitmap:Bitmap?)->Unit) {
29 | Thread {
30 | val bitmap = load(filename)
31 | Handler(Looper.getMainLooper()).post { callback(bitmap) }
32 | }.start()
33 | }
34 |
35 | @SuppressWarnings("loading bitmap in main thread not recommended (use saveAsync)")
36 | fun save(
37 | filename: String,
38 | bitmap: Bitmap,
39 | quality:Int=100,
40 | width:Int=bitmap.width,
41 | height:Int=bitmap.height
42 | ) {
43 | val path = "$root/$filename"
44 | val out = FileOutputStream(path)
45 | val resized = Bitmap.createScaledBitmap(bitmap,width , height, false)
46 | resized.compress(Bitmap.CompressFormat.PNG, quality, out)
47 | out.flush()
48 | out.close()
49 | }
50 |
51 | fun saveAsync(
52 | filename: String,
53 | bitmap: Bitmap,
54 | quality:Int=100,
55 | width:Int=bitmap.width,
56 | height:Int=bitmap.height,
57 | callback: ()->Unit
58 | ) {
59 | Thread {
60 | save(filename, bitmap,quality,width,height)
61 | Handler(Looper.getMainLooper()).post { callback() }
62 | }.start()
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ali77gh/easydataexample/User.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydataexample
2 |
3 | import android.content.Context
4 | import com.ali77gh.easydata.IORun
5 | import com.ali77gh.easydata.sqlite.EasyTable
6 | import com.ali77gh.easydata.sqlite.Model
7 |
8 | /**
9 | * Created by ali on 8/23/18.
10 | */
11 |
12 | class Loc(val lat:Double,val lng:Double)
13 | class User(
14 | override var id: String,
15 | var hashPass: String,
16 | var name: String,
17 | var age :Int,
18 | var role:String,
19 | var money:Int,
20 | var marks :List?=null,
21 | var locations: List?=null
22 | ) : Model {
23 |
24 | class UserTable(context: Context) :
25 | EasyTable(context, User::class.java,autoSetId = false) {
26 |
27 | // custom queries here
28 | fun getByName(name: String) = filter { it.name==name }
29 |
30 | val admins get() = filter { it.role=="admin" }
31 |
32 | fun isAdmin(id: String) = any { it.id==id && it.role=="admin" } // reusable
33 |
34 | val top5Richs get() = sortedByDescending { it.money }.subList(0,5)
35 |
36 | fun checkPassword(id:String, hashPass:String) = getById(id)!!.hashPass==hashPass
37 |
38 | fun isUnderAge(id:String) = any{ it.id==id && it.age<18 }
39 |
40 | fun removeUnderAges() = deleteWhere { it.age < 18 }
41 |
42 | fun increaseAges1() = updateAll {
43 | it.age++
44 | return@updateAll it
45 | }
46 |
47 | fun increaseAges2() = updateAll {it.age++;it}
48 |
49 | fun increaseAges3() = updateAll {it.apply{ age++ }}
50 |
51 | fun increaseRoleOfAlis() = updateWhere({it.name=="ali"},{it.role="admin";it})
52 |
53 | fun asyncGetByName(name:String,cb:(user: User)->Unit)
54 | = IORun( {filter { it.name==name }[0]} , cb )
55 |
56 | }
57 |
58 | // repo singleton
59 | companion object {
60 | private var repo: UserTable? = null
61 | fun getRepo(context: Context): UserTable {
62 | if (repo ==null) repo = UserTable(context)
63 | return repo!!
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Built application files
3 | *.apk
4 | *.aar
5 | *.ap_
6 | *.aab
7 |
8 | # Files for the ART/Dalvik VM
9 | *.dex
10 |
11 | # Java class files
12 | *.class
13 |
14 | # Generated files
15 | bin/
16 | gen/
17 | out/
18 | # Uncomment the following line in case you need and you don't have the release build type files in your app
19 | # release/
20 |
21 | # Gradle files
22 | .gradle/
23 | build/
24 |
25 | # Local configuration file (sdk path, etc)
26 | local.properties
27 |
28 | # Proguard folder generated by Eclipse
29 | proguard/
30 |
31 | # Log Files
32 | *.log
33 |
34 | # Android Studio Navigation editor temp files
35 | .navigation/
36 |
37 | # Android Studio captures folder
38 | captures/
39 |
40 | # IntelliJ
41 | *.iml
42 | .idea/workspace.xml
43 | .idea/tasks.xml
44 | .idea/gradle.xml
45 | .idea/assetWizardSettings.xml
46 | .idea/dictionaries
47 | .idea/libraries
48 | .idea/jarRepositories.xml
49 | # Android Studio 3 in .gitignore file.
50 | .idea/caches
51 | .idea/modules.xml
52 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
53 | .idea/navEditor.xml
54 |
55 | # Keystore files
56 | # Uncomment the following lines if you do not want to check your keystore files in.
57 | #*.jks
58 | #*.keystore
59 |
60 | # External native build folder generated in Android Studio 2.2 and later
61 | .externalNativeBuild
62 | .cxx/
63 |
64 | # Google Services (e.g. APIs or Firebase)
65 | # google-services.json
66 |
67 | # Freeline
68 | freeline.py
69 | freeline/
70 | freeline_project_description.json
71 |
72 | # fastlane
73 | fastlane/report.xml
74 | fastlane/Preview.html
75 | fastlane/screenshots
76 | fastlane/test_output
77 | fastlane/readme.md
78 |
79 | # Version control
80 | vcs.xml
81 |
82 | # lint
83 | lint/intermediates/
84 | lint/generated/
85 | lint/outputs/
86 | lint/tmp/
87 | # lint/reports/
88 |
89 | # Android Profiling
90 | *.hprof
91 |
92 |
93 | # added by ali:
94 | .settings/
95 | .project
96 | app/.classpath
97 | app/.project
98 | app/.settings/
99 | easydata/.classpath
100 | easydata/.project
101 | easydata/.settings/
102 | easyrepolib/libs/jars/mylib.jar
103 | .idea/
104 | .README.md.swp
105 | gradle.properties
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/sqlite/EasyTable.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.sqlite
2 |
3 | import android.content.Context
4 | import java.util.*
5 | import kotlin.collections.ArrayList
6 |
7 | abstract class EasyTable(
8 | context: Context,
9 | private val type: Class,
10 | tableName: String = type.simpleName,
11 | private var autoSetId: Boolean = false
12 | ) : KeyValDb(context,tableName) , Iterable{
13 |
14 | // Insert
15 | open fun insert(row: T) {
16 | if (autoSetId) row.id = UUID.randomUUID().toString()
17 | add(row.id, row)
18 | }
19 |
20 | open fun insertMany(rows:Iterable) = rows.forEach { insert(it) }
21 |
22 | // Update
23 | open fun update(row: T) = super.update(row.id, row)
24 |
25 | open fun updateMany(rows: Iterable) = rows.forEach { update(it.id, it) }
26 |
27 | open fun updateAll(change:(row:T)->T) = updateMany(map(change))
28 |
29 | open fun updateWhere(condition: (obj: T) -> Boolean, change:(row:T)->T)
30 | = updateMany(filter(condition).map(change))
31 |
32 |
33 | //Delete
34 | open fun deleteWhere(condition: (obj: T) -> Boolean)
35 | = deleteMany(filter(condition).map { it.id }.toTypedArray())
36 |
37 |
38 | //Read
39 | open fun toList(): ArrayList {
40 | val list = ArrayList()
41 | val itr = iterator()
42 | while (itr.hasNext())
43 | list.add(itr.next())
44 | return list
45 | }
46 |
47 | /**
48 | * @return null if not found
49 | */
50 | open fun getById(id: String) = gson.fromJson(super.getByIdStr(id),type)
51 |
52 | /**
53 | * this is faster then filter { it.id = ids.contains() }
54 | */
55 | open fun getByIds(id: List) = super.getByIdsStr(id).map { gson.fromJson(it,type) }
56 |
57 | /**
58 | * @return null if not found
59 | */
60 | open fun getOne(condition: (obj: T) -> Boolean):T?{
61 | for(row in this)
62 | if (condition(row)) return row
63 | return null
64 | }
65 |
66 | override fun iterator(): Iterator {
67 | val itr = super.innerIterator()
68 | return object : Iterator {
69 | override fun hasNext(): Boolean {
70 | return itr.hasNext()
71 | }
72 |
73 | override fun next(): T {
74 | return gson.fromJson(
75 | itr.next(),
76 | type
77 | ) as T
78 | }
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/sqlite/KeyValDb.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.sqlite
2 |
3 | import android.content.ContentValues
4 | import android.content.Context
5 | import android.database.CursorIndexOutOfBoundsException
6 | import com.google.gson.Gson
7 | import kotlin.collections.ArrayList
8 |
9 | /**
10 | * Created by ali on 9/17/18.
11 | */
12 | open class KeyValDb(
13 | context: Context,
14 | private var table: String="default"
15 | ){
16 |
17 | private val db = context.openOrCreateDatabase("easydb", Context.MODE_PRIVATE, null)
18 | val gson: Gson = Gson()
19 |
20 | init {
21 | db.execSQL("CREATE TABLE IF NOT EXISTS $table(id VARCHAR PRIMARY KEY,value VARCHAR);")
22 | }
23 |
24 | fun add(id: String, obj: Any){
25 | db.execSQL("INSERT INTO $table VALUES('$id','${gson.toJson(obj)}');")
26 | }
27 |
28 | val isEmpty: Boolean get() {
29 | val resultSet = db.rawQuery("Select * from $table;", null)
30 | resultSet.moveToFirst()
31 | return try {
32 | resultSet.getString(1)
33 | false
34 | } catch (e: CursorIndexOutOfBoundsException) {
35 | true
36 | }
37 | }
38 |
39 | protected fun update(id: String, obj: Any) {
40 | val contentValues = ContentValues().apply {
41 | put("value", gson.toJson(obj))
42 | }
43 | db.update(table, contentValues, "id = ? ", arrayOf(id))
44 | }
45 |
46 | protected fun getByIdStr(id:String):String?{
47 | val cursor = db.rawQuery("Select value from $table where id='$id';", null)!!
48 | cursor.moveToFirst()
49 | if (cursor.isAfterLast) return null
50 | return cursor.getString(0)
51 | }
52 |
53 | /*
54 | * this is faster way because have no gson deserialize insides
55 | */
56 | protected fun getByIdsStr(ids:List):List{
57 | val idsMap = ids.associateBy { it }
58 | val result = ArrayList()
59 |
60 | val cursor = db.rawQuery("Select * from $table;", null)!!
61 | cursor.moveToFirst()
62 | while (!cursor.isAfterLast)
63 | if (idsMap.containsKey(cursor.getString(0)))
64 | result.add(cursor.getString(1))
65 |
66 | return result
67 | }
68 |
69 | fun delete(id: String) = db.delete(table, "id = ? ", arrayOf(id))
70 |
71 | protected fun deleteMany(ids: Array) = ids.forEach { delete(it) }
72 |
73 | fun drop() = db.execSQL("DROP TABLE IF EXISTS $table")
74 |
75 |
76 | // Iterable APIs
77 |
78 | protected fun innerIterator() :Iterator{
79 | val resultSet = db.rawQuery("Select value from $table;", null)!!
80 | resultSet.moveToFirst()
81 | return object : Iterator {
82 | override fun hasNext(): Boolean {
83 | return !resultSet.isAfterLast
84 | }
85 |
86 | override fun next(): String {
87 | val str = resultSet.getString(0)
88 | resultSet.moveToNext()
89 | return str
90 | }
91 | }
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/easydata/src/main/java/com/ali77gh/easydata/sqlite/FastTable.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydata.sqlite
2 |
3 | import android.content.Context
4 | import kotlin.collections.ArrayList
5 | import kotlin.collections.HashMap
6 |
7 | abstract class FastTable(
8 | context: Context,
9 | private val type: Class,
10 | tableName: String = type.simpleName,
11 | autoSetId: Boolean = false
12 | ) : EasyTable(context ,type,tableName,autoSetId){
13 |
14 | // private var IOWriteTasks = ArrayList<()->Unit>()
15 |
16 | private val cache : HashMap
17 |
18 | init {
19 | if (CachePool.containsKey(tableName))
20 | cache = CachePool[tableName] as HashMap
21 | else{
22 | cache = HashMap()
23 | for (row in super.toList())
24 | cache[row.id] = row
25 | CachePool[tableName] = cache as HashMap
26 | }
27 |
28 | // Thread{
29 | // while (true){
30 | // if (IOWriteTasks.size != 0){
31 | // IOWriteTasks[0].invoke()
32 | // IOWriteTasks.removeAt(0)
33 | // } else Thread.sleep(100) // CPU need to sleep sometimes :) (for battery life)
34 | // }
35 | // }.start()
36 | }
37 |
38 | override fun insert(row: T) {
39 | cache[row.id] = row
40 | super.insert(row)
41 | }
42 |
43 | override fun insertMany(rows:Iterable){
44 | for (row in rows)
45 | cache[row.id] = row
46 | super.insertMany(rows)
47 | }
48 |
49 | // Update
50 | override fun update(row: T){
51 | cache[row.id] = row
52 | super.update(row)
53 | }
54 |
55 | override fun updateMany(rows: Iterable){
56 | for (row in rows)
57 | cache[row.id] = row
58 | super.updateMany(rows)
59 | }
60 |
61 | override fun updateAll(change:(row:T)->T){
62 | for (row in this)
63 | cache[row.id] = change(row)
64 | super.updateAll(change)
65 | }
66 |
67 | override fun updateWhere(condition: (obj: T) -> Boolean, change:(row:T)->T){
68 | super.updateWhere(condition, change)
69 | for (row in this)
70 | if (condition(row))
71 | cache[row.id] = change(row)
72 | }
73 |
74 |
75 | //Delete
76 | override fun deleteWhere(condition: (obj: T) -> Boolean){
77 | super.deleteWhere(condition) // this will not work if you put it after loop
78 | for (row in this.toList())
79 | if (condition(row))
80 | cache.remove(row.id)
81 | }
82 |
83 | //Read
84 | override fun toList() = ArrayList().apply {
85 | for (row in this@FastTable)
86 | this@apply.add(row)
87 | }
88 |
89 | /**
90 | * @return null if not found
91 | * */
92 | override fun getById(id: String):T?{
93 | return cache[id]
94 | }
95 |
96 | /**
97 | * @return null if not found
98 | * */
99 | override fun getOne(condition: (obj: T) -> Boolean):T?{
100 | val iterator = this.iterator()
101 | while (iterator.hasNext()){
102 | val row = iterator.next()
103 | if (condition(row)) return row
104 | }
105 | return null
106 | }
107 |
108 | override fun iterator(): Iterator {
109 | val iterator = cache.iterator()
110 | return object : Iterator {
111 |
112 | override fun hasNext() = iterator.hasNext()
113 |
114 | override fun next() : T = iterator.next().value
115 | }
116 | }
117 |
118 | companion object{
119 | private val CachePool = HashMap>()
120 |
121 | fun clearCache(tableName:String){
122 | CachePool.remove(tableName)
123 | }
124 | fun clearCache(type:Class){
125 | clearCache(type.simpleName)
126 | }
127 | }
128 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ali77gh/easydataexample/TestActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ali77gh.easydataexample
2 |
3 | import android.app.Activity
4 | import android.graphics.Bitmap
5 | import android.graphics.BitmapFactory
6 | import android.os.Bundle
7 | import android.util.Log
8 | import android.widget.ImageView
9 | import android.widget.TextView
10 | import com.ali77gh.easydataexample.R
11 | import com.ali77gh.easydata.repos.*
12 | import com.ali77gh.easydata.security.DeviceKeyGenerator
13 | import com.google.gson.Gson
14 | import java.util.*
15 | import kotlin.system.measureTimeMillis
16 |
17 | class TestActivity : Activity() {
18 | private var log: TextView? = null
19 | private var image: ImageView? = null
20 |
21 | // Tools
22 | private val bitmapForTest: Bitmap
23 | get() = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
24 | private fun log(msg: String){
25 | Log.d("test",msg)
26 | log!!.append("$msg\n")
27 | }
28 | private fun logTitle(title:String) = log("\n----- $title -----")
29 | private fun test(name:String,bool:Boolean) = log(name+"\t\t\t" + if (bool) " passed!" else " failed! ")
30 | private fun test(name:String,a:Any,b:Any) = test(name,a==b)
31 | fun logUI(msg:String)=runOnUiThread { log(msg) }
32 |
33 | override fun onCreate(savedInstanceState: Bundle?) {
34 | super.onCreate(savedInstanceState)
35 | setContentView(R.layout.activity_test)
36 | log = findViewById(R.id.logger)
37 | image = findViewById(R.id.image)
38 |
39 | // bitmapTest()
40 | // byteTest()
41 | // stringTest()
42 | // objectTest()
43 | // genericDAOTest()
44 | // genericDAOTestHeavy()
45 | // testSafeBox()
46 | // performanceTest()
47 | testSettings()
48 | }
49 |
50 | private fun bitmapTest() {
51 | logTitle("bitmap test")
52 |
53 | val bitmapDAO = BitmapDAO(this, RootMode.LOCAL)
54 |
55 | // Sync
56 | bitmapDAO.save("testBitmap.bmp",bitmapForTest,100)
57 | test("testBitmap.bmp",bitmapDAO.checkExist("testBitmap.bmp"))
58 |
59 | bitmapDAO.save("testBitmap50.bmp",bitmapForTest,50)
60 | test("testBitmap50.bmp",bitmapDAO.checkExist("testBitmap50.bmp"))
61 |
62 | bitmapDAO.save("testBitmap10px.bmp",bitmapForTest, width=10, height=10)
63 | test("testBitmap10px.bmp",bitmapDAO.checkExist("testBitmap10px.bmp"))
64 |
65 | // Async
66 | bitmapDAO.saveAsync("testBitmapAsync.bmp", bitmapForTest, callback = {
67 | test("testBitmapAsync.bmp",bitmapDAO.checkExist("testBitmapAsync.bmp"))
68 | })
69 | Thread.sleep(1000)
70 | log("bitmap tests all done!")
71 | }
72 |
73 | private fun byteTest() {
74 |
75 | logTitle("byte test")
76 |
77 | val byteDAO = ByteDAO(this, RootMode.LOCAL)
78 | val bytes = ByteArray(4)
79 | bytes[0] = 1
80 | bytes[1] = 1
81 | bytes[2] = 0
82 | bytes[3] = 1
83 |
84 | // saving
85 | byteDAO.save("test", bytes)
86 | test("saving",byteDAO.checkExist("test"))
87 |
88 | // loading
89 | val loadedBytes: ByteArray = byteDAO.load("test")!!
90 | test("byte eq:",loadedBytes[0],bytes[0])
91 |
92 | // removing
93 | byteDAO.remove("test")
94 | test("removing",!byteDAO.checkExist("test"))
95 | }
96 |
97 | private fun stringTest() {
98 |
99 | logTitle("string test")
100 |
101 | val stringDAO = StringDAO(this, RootMode.LOCAL)
102 | val string = "write this file"
103 |
104 | //saving
105 | stringDAO.save("test", string)
106 | test("saving",stringDAO.checkExist("test"))
107 |
108 | // loading
109 | val loadedString: String = stringDAO.load("test")
110 | test("byte eq:",loadedString,string)
111 |
112 | // removing
113 | stringDAO.remove("test")
114 | test("removing",!stringDAO.checkExist("test"))
115 |
116 | // default
117 | test("default",stringDAO.load("test","def"),"def")
118 | }
119 |
120 | private fun objectTest() {
121 |
122 | logTitle("object test")
123 |
124 | val dao = ObjectDAO(this, RootMode.LOCAL)
125 | val user = User("id","pass","ali",18,"admin",1000)
126 |
127 | dao.save("test", user)
128 | test("saving",dao.checkExist("test"))
129 |
130 | // loading
131 | val loaded: User = dao.load("test", User::class.java) as User
132 | test("equality:",loaded.name,user.name)
133 |
134 | // removing
135 | dao.remove("test")
136 | test("removing",!dao.checkExist("test"))
137 | }
138 |
139 | private fun genericDAOTest() {
140 |
141 | logTitle("generic test")
142 | val users = User.getRepo(this)
143 | val g = Gson()
144 |
145 | log("---before delete---")
146 | for(user in users)
147 | log(user.name)
148 |
149 | users.deleteWhere { true }
150 |
151 | log("---after delete---")
152 | for(user in users)
153 | log(user.name)
154 |
155 | //insert
156 | val u = User(UUID.randomUUID().toString(),"pass","ali",18,"admin",1000)
157 | users.insert(u)
158 |
159 | log("---after insert---")
160 | for(user in users)
161 | log(user.id)
162 |
163 | test("insert",users.getById(u.id)!!.name,"ali")
164 |
165 | //read
166 | test("read",users.toList().size,1)
167 |
168 | //update
169 | u.name = "hasan"
170 | users.update(u)
171 | test("update",users.getById(u.id)!!.name,"hasan")
172 |
173 | //delete
174 | users.deleteWhere { it.id==u.id }
175 | test("delete",users.toList().size,0)
176 |
177 | }
178 |
179 | private fun genericDAOTestHeavy() {
180 |
181 | logTitle("generic test heavy")
182 |
183 | val users = User.getRepo(this)
184 | users.deleteWhere { true }
185 |
186 | log("---after delete all")
187 | for(user in users)
188 | log(user.id)
189 |
190 | users.filter { true }
191 |
192 | //insert
193 | users.insert(User(UUID.randomUUID().toString(),"pass","ali",18,"admin",1000))
194 | users.insert(User(UUID.randomUUID().toString(),"pass","ali",18,"admin",1000))
195 | users.insert(User(UUID.randomUUID().toString(),"pass","ali",18,"admin",1000))
196 | users.insert(User(UUID.randomUUID().toString(),"pass","ali",18,"admin",1000))
197 |
198 | log("---after 4 inserts---")
199 | for(user in users)
200 | log(user.id)
201 |
202 | log("---filter---")
203 | for(user in users.filter { it.id.contains("e") })
204 | log(user.id)
205 |
206 | log(users.sumBy { it.age }.toString())
207 | }
208 |
209 | private fun performanceTest(){
210 |
211 | logTitle("performance")
212 |
213 | Thread{
214 | val users = User.getRepo(this)
215 |
216 | logUI("users size: ${users.toList().size}")
217 |
218 | val L = 10000
219 | val g = Gson()
220 | val serializeTime = measureTimeMillis {
221 | for(i in 0..L){
222 | val user = User(UUID.randomUUID().toString(),"pass","ali",18,"admin",1000)
223 | g.toJson(user)
224 | }
225 | }
226 | logUI("serializeTime: $serializeTime")
227 | val user = User(UUID.randomUUID().toString(),"pass","ali",18,"admin",1000)
228 | val str = g.toJson(user)
229 | val deserializeTime = measureTimeMillis {
230 | for(i in 0..L){
231 | g.fromJson(str, User::class.java)
232 | }
233 | }
234 | logUI("deserializeTime: $deserializeTime")
235 |
236 | val r = Random()
237 | val insertTime = measureTimeMillis {
238 | for(i in 0..L)
239 | users.insert(User(UUID.randomUUID().toString(),"pass","ali",18,"admin",1000))
240 | }
241 | logUI("users size: ${users.toList().size}")
242 | logUI(" insert: $insertTime ms")
243 |
244 | val lastId = users.toList().last().id
245 | val getById = measureTimeMillis {
246 | users.getById(lastId)
247 | }
248 | logUI(" getById: $getById ms")
249 |
250 | val getWithFalsyFilter = measureTimeMillis {
251 | users.filter { false }
252 | }
253 | logUI(" getWithFalsyFilter: $getWithFalsyFilter ms")
254 |
255 | val readAllTime = measureTimeMillis {
256 | users.toList()
257 | }
258 | logUI(" readAllTime: $readAllTime ms")
259 |
260 | val readWithFilterTime = measureTimeMillis {
261 | users.filter { it.name=="ali" }
262 | }
263 | logUI(" readWithFilterTime: $readWithFilterTime ms")
264 | val readWithFilterAndMapTime = measureTimeMillis {
265 | users.filter { it.name=="ali" }.map { it.role }
266 | }
267 | logUI(" readWithFilterAndMapTime: $readWithFilterAndMapTime ms")
268 | val readWithMapAndFilterTime = measureTimeMillis {
269 | users.map { it.name }.filter { it=="ali" }
270 | }
271 | logUI(" readWithMapAndFilterTime: $readWithMapAndFilterTime ms")
272 | val updateAll = measureTimeMillis {
273 | users.updateWhere({it.name=="ali"},{it.apply { it.age=23 }})
274 | }
275 | logUI(" updateAll: $updateAll ms")
276 |
277 | val updateOne = measureTimeMillis {
278 | users.updateWhere({it.id==lastId},{it.apply { it.age=25 }})
279 | }
280 | logUI(" updateOne: $updateOne ms")
281 |
282 | val removeOne = measureTimeMillis {
283 | users.deleteWhere { it.id==lastId }
284 | }
285 | logUI(" removeOne: $removeOne ms")
286 |
287 | val removeAll = measureTimeMillis {
288 | users.deleteWhere { true }
289 | }
290 | logUI(" removeAll: $removeAll ms")
291 | }.start()
292 | }
293 |
294 | private fun testSafeBox() {
295 | logTitle("safebox test")
296 | val key = DeviceKeyGenerator.Generate(this)
297 | val safeBox = SafeBox(this, key)
298 |
299 | safeBox.save("password", "myPassword")
300 | var readed = safeBox.load("password")
301 | test("read eq",readed=="myPassword")
302 |
303 | safeBox.save("password", "سلام")
304 | readed = safeBox.load("password")
305 | test("utf8",readed=="سلام")
306 | }
307 |
308 | private fun testSettings(){
309 | logTitle("settings test")
310 | val now = Date().time
311 |
312 | Settings.get(this).theme = "dark"
313 | Settings.get(this).dateSystem = Settings.DateSystem.Gregorian
314 | Settings.get(this).notification = true
315 | Settings.get(this).lastLogin = now
316 |
317 | test("theme", Settings.get(this).theme, "dark")
318 | test("dateSystem", Settings.get(this).dateSystem, Settings.DateSystem.Gregorian)
319 | test("notif", Settings.get(this).notification, true)
320 | test("lastLogin", Settings.get(this).lastLogin, now)
321 | }
322 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EasyDataAndroid
2 |
3 |
4 |
5 | [](https://www.jitpack.io/#ali77gh/EasyDataAndroid)
6 |
7 |
8 | Painless android library for working with files and database in easiest way as possible.
9 | Sqlite ORM + handy tools for saving and restoring any kind of data.
10 |
11 |
12 |
13 | # Features
14 | 1. No SQL knowledge needed. ✅
15 | 2. Easy to setup and no bloody🩸 compile time code generations. ✅
16 | 3. Supports nested objects and arrays. ✅
17 | 4. Easy migration with no pain while app updates and schema changes. ✅
18 | 5. Most complex queries are possible and easy to write with simple functions. ✅
19 | 6. Async IO and Multi-Threading supported. ✅
20 | 7. Kotlin programming language recommended. ✅
21 | 8. Supports old android versions (min sdk 15) ✅
22 |
23 |
24 |
25 | # Documentation
26 | 1. [Installation](#gradle)
27 | 2. ORM
28 | 1. [Setup your model](#setup-model)
29 | 2. [Simple CRUD](#simple-crud)
30 | 3. [More complex queries (with high order functions 😉)](#queries)
31 | 4. [FastTable (caching system) x10 speed ✈️](#fasttable)
32 | 5. [Nested object or array (no problem)](#nest)
33 | 6. [Migration (no problem)](#migration)
34 | 7. [IORun (handy multi-threading and AsyncIO tool)](#iorun)
35 | 8. [Performance](#performance)
36 | 9. [Under the hood](#under-the-hood)
37 | 10. [examples](./app/src/main/java/com/ali77gh/easydataexample)
38 | 3. [Settings data save and load best practice (without ORM setup)](./app/src/main/java/com/ali77gh/easydataexample/Settings.kt)
39 | 4. [SafeBox](#safebox)
40 | 5. [Working with files](#working-with-files)
41 | 1. [BitmapDAO](#bitmapdao)
42 | 2. [ByteDAO](#bytedao)
43 | 3. [ObjectDAO](#objectdao)
44 | 4. [StringDAO](#stringdao)
45 | 6. [License](#license)
46 |
47 | # gradle
48 | Add it in your root build.gradle at the end of repositories:
49 | ```groovy
50 | allprojects {
51 | repositories {
52 | // add this line
53 | maven { url 'https://www.jitpack.io' }
54 | }
55 | }
56 | ```
57 | Step 2. Add the dependency (in your app build.gradle):
58 | ```groovy
59 | dependencies {
60 | // add this line
61 | implementation 'com.github.ali77gh:EasyDataAndroid:3.2.2'
62 | }
63 | ```
64 |
65 | # Setup model
66 | Step 1: all you need to do is implement "Model" interface
67 | note: you need to have "id" field this will act like unique primary key
68 | ```kotlin
69 | class User(
70 | override var id: String,
71 | var hashPass: String,
72 | var name: String,
73 | var age :Int,
74 | var role:String,
75 | var money:Int,
76 | ) : Model
77 | ```
78 |
79 |
80 | Step 2: Make a Table class of your model that is extending EasyTable (you can also put in inside User.kt if you want)
81 | ```kotlin
82 | class UserTable(context: Context) :
83 | EasyTable(context, User::class.java,autoSetId = false){
84 |
85 | // your custom query here
86 | }
87 | // no need to put any code inside this class
88 | // anyway we will put some custom queries inside of this class later
89 | ```
90 | autoSetId: this will generate random UUID while you insert a row (set it false if you have plan for id yourself or you are getting rows from server that already have id)
91 |
92 | That's it 😉 now you are ready to go.
93 |
94 |
95 |
96 | # Simple CRUD
97 |
98 | Make table class instance:
99 | ```kotlin
100 | val users = UserTable(context)
101 | ```
102 |
103 | Insert:
104 | ```kotlin
105 | val user = User(...)
106 | users.insert(user)
107 | ```
108 |
109 | Read:
110 | ```kotlin
111 | val user = users.getById("id")
112 | // you can also loop on users
113 | for (user in users){
114 | //do something with user
115 | }
116 | // note: its not loading all users on ram (check out under the hood part for more information)
117 | // see complex queries part for More reading samples
118 | ```
119 |
120 | Update:
121 | ```kotlin
122 | user.name = "alireza"
123 | users.update(user) // it find user row by id and apply changes
124 | ```
125 |
126 | Delete:
127 | ```kotlin
128 | // delete one row by id
129 | users.delete(user.id)
130 |
131 | // delete many rows with where statement
132 | users.deleteWhere { it.name=="ali" }
133 | ```
134 |
135 | I recommend you to write singleton (optional)
136 | ```kotlin
137 | // repo singleton
138 | companion object {
139 | private var repo: UserTable? = null
140 | fun getRepo(context: Context): UserTable {
141 | if (repo ==null) repo = UserTable(context)
142 | return repo!!
143 | }
144 | }
145 | // ------
146 | // then you can get table class like this:
147 | var users = UserTable.getRepo(context)
148 | ```
149 |
150 | Important warning ☢ ️: don't forget to star ⭐ this repo
151 |
152 |
153 | # Queries
154 | Now its time to come back to Table class and add some queries.
155 | Note: You can write this queries right after users object too. but putting your queries here in Table class make your code cleaner.
156 | Checkout samples:
157 |
158 | ```kotlin
159 | class UserTable(context: Context) :
160 | EasyTable(context, User::class.java, autoSetId = false) {
161 |
162 | // custom queries here.
163 |
164 | // you can access to all of standard high order functions on lists.
165 | fun getByName(name: String) = filter { it.name == name }
166 |
167 | // you can use 'get' keyword when you have no input param for query.
168 | val admins get() = filter { it.role == "admin" }
169 |
170 | // query can return any type (here is boolean).
171 | fun isAdmin(id: String) = any { it.id == id && it.role == "admin" }
172 |
173 | fun isUnderAge(id: String) = any { it.id == id && it.age < 18 }
174 |
175 | // you can write almost every queries in one line.
176 | val top5Riches get() = sortedByDescending { it.money }.subList(0, 5)
177 |
178 | // but you are free to write multi line query and use variables if you want.
179 | val top5Riches
180 | get() {
181 | val sorted = sortedByDescending { it.money }
182 | val top5 = sorted.subList(0, 5)
183 | return top5
184 | }
185 |
186 |
187 | // here is a check password and i have nothing to say ;)
188 | fun checkPassword(id: String, hashPass: String) = getById(id)!!.hashPass == hashPass
189 |
190 |
191 | // lets delete some rows with where statement
192 | fun removeUnderAges() = deleteWhere { it.age < 18 }
193 |
194 |
195 | // update all
196 | // 'it' object is before update and what you return will replace with old one
197 | fun increaseAges1() = updateAll {
198 | it.age++
199 | return@updateAll it
200 | }
201 |
202 | // same query in other style
203 | fun increaseAges2() = updateAll { it.age++;it }
204 |
205 | // again same query in other style (you are free to write query in your way)
206 | fun increaseAges3() = updateAll { it.apply { age++ } }
207 |
208 | // lets do some update with where statement
209 | // first function returns boolean and 'true' means this row should update
210 | // second function effects on that row (same in updateAll function)
211 | fun increaseRoleOfAlis() = updateWhere({ it.name == "ali" }, { it.role = "admin";it })
212 |
213 | // IORun will run your code in other thread and pass result back to UI thread with callback
214 | // you can use it in other replaces too (not limited to writing queries)
215 | fun asyncGetByName(name: String, cb: (user: User) -> Unit) = IORun({ filter { it.name == name }[0] }, cb)
216 |
217 | }
218 |
219 | ```
220 |
221 |
222 |
223 | # FastTable
224 |
225 | This is a caching system and will speed up your read queries.
226 | if you want to enable FastTable all you have to do is extending FastTable instead EasyTable:
227 |
228 | ```kotlin
229 | class UserTable(context: Context) :
230 | FastTable(context, User::class.java,autoSetId = false)
231 | ```
232 |
233 | You have exact same functionality but faster read queries [see performance part](#performance)
234 | Memory Usage: FastTable use 2MB more RAM with 10,000 of User object that we shows in this documentation.
235 |
236 |
237 |
238 | # Nest
239 | You can have a field in type of list or custom class or list of custom class or even more nest levels.
240 | checkout this:
241 | ```kotlin
242 | class Loc(val lat:Double,val lng:Double)
243 | class User(
244 | override var id: String,
245 | var hashPass: String,
246 | var name: String,
247 | var age :Int,
248 | var role:String,
249 | var money:Int,
250 | var marks :List,
251 | var locations: List
252 | ) : Model
253 | ```
254 | handy? right?
255 |
256 |
257 | # Migration
258 |
259 | No problem.
260 | Just add field (nullable) and everything will work as well.
261 | Warning: lets say you insert a rowA to table and then you change schema by adding fieldX. then fieldX of rowA will be null
262 | Recommended way to handle this:
263 | add init{} function in your model and set a default value for fieldX if is null.
264 |
265 |
266 |
267 | # IORun
268 | Its easier way to run a code in other thread and pass result to Main thread (UI thread).
269 | Checkout this:
270 | ```kotlin
271 | IORun({
272 | //do heavy process or IO operation
273 | return result
274 | } , { result ->
275 | // this will run in ui thread
276 | // and you can do things with result object
277 | // object can have any type based on what you return in first function
278 | })
279 | ```
280 |
281 |
282 |
283 | # Performance
284 | Its for 10,000 record on android virtualbox on my dual core laptop.
285 | [See running code of this test](./app/src/main/java/com/ali77gh/easydataexample/TestActivity.kt)
286 |
287 |
288 |
289 | FastTable use 2MB more RAM with 10,000 of User object that we shows in this documentation
290 |
291 |
292 |
293 | # Under the hood
294 |
295 | This library use sqlite and json to saving and restoring records.
296 | [KeyValDb](./easydata/src/main/java/com/ali77gh/easydata/sqlite/KeyValDb.kt) class handle all SQL stuff.
297 | [EasyTable](./easydata/src/main/java/com/ali77gh/easydata/sqlite/EasyTable.kt) class is extending KeyValDb and have json serialize and deserialize and generic things and also iteration stuff that provides high order functions on sqlite cursor.
298 | [FastTable](./easydata/src/main/java/com/ali77gh/easydata/sqlite/FastTable.kt) class is extending EasyTable and have some caching stuff for better read speed. see [performance](#performance)
299 |
300 |
301 |
302 | # SafeBox
303 | This class used for saving password and other sensitive data.
304 | This class actually encrypt and saves data in app local storage.
305 | Checkout this:
306 | ```kotlin
307 | // This functions generates key with device unique id
308 | // this line is optional and you can use any string as a key
309 | val key = DeviceKeyGenerator.Generate(this)
310 |
311 | // Safe box instance
312 | val safeBox = SafeBox(this, key)
313 |
314 | safeBox.save("password", "secret")
315 | var readed = safeBox.load("password")
316 | ```
317 |
318 |
319 |
320 | # Working with files
321 | This tools provide Read , Write , Delete for you with Sync or Async mode on External , Local , Cache Storage
322 | RootModes : LOCAL , EXTERNAL , CACHE
323 | LOCAL: app private storage (user and other apps can not access)
324 | EXTERNAL: root of public storage (user and other apps can access)
325 | CACHE: files in this mode will be removed by cleaner apps
326 |
327 |
328 | # BitmapDAO
329 | ```kotlin
330 | // instance
331 | val bitmapDAO = BitmapDAO(this, RootMode.LOCAL) // see RootMode in Working with files section
332 | // simple save
333 | bitmapDAO.save("testBitmap.bmp",bitmap)
334 | // save with 50% of quality
335 | bitmapDAO.save("testBitmap50.bmp",bitmapForTest,50)
336 | // resize and save with given width and height
337 | bitmapDAO.save("testBitmap10px.bmp",bitmapForTest, width=10, height=10)
338 | // async save
339 | bitmapDAO.saveAsync("testBitmapAsync.bmp", bitmapForTest, callback = {
340 | // done!
341 | })
342 | // simple load
343 | val bitmap = bitmapDAO.load("testBitmap.bmp")
344 | // async load
345 | bitmapDAO.load("testBitmap.bmp",{ bitmap->
346 |
347 | })
348 | ```
349 |
350 |
351 |
352 |
353 | # BytesDAO
354 | ```kotlin
355 | // instance
356 | val bytesDAO = BytesDAO(this, RootMode.LOCAL) // see RootMode in Working with files section
357 | // simple save
358 | bytesDAO.save("test", byteArray)
359 | // async save
360 | bytesDAO.saveAsync("test", byteArray,callback = {
361 | // done!
362 | })
363 | // simple load
364 | val byteArray = bytesDAO.load("test")
365 | // async load
366 | bytesDAO.load("test", { bytes ->
367 |
368 | })
369 | ```
370 |
371 |
372 |
373 | # StringDAO
374 |
375 | ```kotlin
376 | // instance
377 | val stringDAO = StringDAO(this, RootMode.LOCAL) // see RootMode in Working with files section
378 | // simple save
379 | stringDAO.save("test", "your string here")
380 | // async save
381 | stringDAO.saveAsync("test", yourString, callback = {
382 | // done!
383 | })
384 | // simple load
385 | val yourString = stringDAO.load("test")
386 | // async load
387 | stringDAO.load("test", { bytes ->
388 |
389 | })
390 | ```
391 |
392 |
393 |
394 |
395 | # ObjectDAO
396 |
397 | ```kotlin
398 | // instance
399 | val objectDAO = ObjectDAO(this, RootMode.LOCAL) // see RootMode in Working with files section
400 | // simple save
401 | objectDAO.save("test", obj)
402 | // async save
403 | objectDAO.saveAsync("test", obj, callback = {
404 | // done!
405 | })
406 | // simple load
407 | val user = objectDAO.load("test",User::class.java) as User
408 | // async load
409 | objectDAO.load("test", { obj ->
410 |
411 | })
412 | ```
413 |
414 |
415 |
416 | # License
417 | [MIT](https://github.com/ali77gh/EasyDataAndroid/blob/master/LICENSE)
--------------------------------------------------------------------------------