Insanely easy way to work with Android Database.

Related tags

Database sugar
Overview

Sugar ORM Build Status Coverage Status Code Triagers Badge

Join the chat at https://gitter.im/satyan/sugar

Insanely easy way to work with Android databases.

Official documentation can be found here - Check some examples below. The example application is provided in the example folder in the source.

Looking for contributors

We need contributors to help maintain this project, ask @satyan for repo permission

Otherwise you can use another ORM, like https://github.com/requery/requery or https://realm.io/

Features

Sugar ORM was built in contrast to other ORM's to have:

  • A simple, concise, and clean integration process with minimal configuration.
  • Automatic table and column naming through reflection.
  • Support for migrations between different schema versions.

Installing

There are four ways to install Sugar:

As a Gradle dependency

This is the preferred way. Simply add:

compile 'com.github.satyan:sugar:1.5'

to your project dependencies and run gradle build or gradle assemble.

As a Maven dependency

Declare the dependency in Maven:

<dependency>
    <groupId>com.github.satyan</groupId>
    <artifactId>sugar</artifactId>
    <version>1.5</version>
</dependency>

As a library project

Download the source code and import it as a library project in Eclipse. The project is available in the folder library. For more information on how to do this, read here.

As a jar

Visit the releases page to download jars directly. You can drop them into your libs folder and configure the Java build path to include the library. See this tutorial for an excellent guide on how to do this.

How to use master version

First, download sugar repository

git clone [email protected]:satyan/sugar.git

include this in your settings.gradle

include ':app' // your module app
include ':sugar'

def getLocalProperty(prop) {
	Properties properties = new Properties()
	properties.load(new File(rootDir.absolutePath + '/local.properties').newDataInputStream())
	return properties.getProperty(prop, '')
}

project(':sugar').projectDir = new File(getLocalProperty('sugar.dir'))

include this in your local.properties

sugar.dir=/path/to/sugar/library

add sugar project to the dependencies of your main project (build.gradle)

dependencies {
    compile project(':sugar')
}

You should also comment this line just comment this line (library/build.gradle): https://github.com/satyan/sugar/blob/master/library%2Fbuild.gradle#L2

// apply from: '../maven_push.gradle'

===================

After installing, check out how to set up your first database and models here Outdated. Check examples of 1.4 and master below:

Examples

SugarRecord

public class Book extends SugarRecord {
  @Unique
  String isbn;
  String title;
  String edition;

  // Default constructor is necessary for SugarRecord
  public Book() {

  }

  public Book(String isbn, String title, String edition) {
    this.isbn = isbn;
    this.title = title;
    this.edition = edition;
  }
}

or

@Table
public class Book { ... }

Save Entity

Book book = new Book("isbn123", "Title here", "2nd edition")
book.save();

or

SugarRecord.save(book); // if using the @Table annotation 

Load Entity

Book book = Book.findById(Book.class, 1);

Update Entity

Book book = Book.findById(Book.class, 1);
book.title = "updated title here"; // modify the values
book.edition = "3rd edition";
book.save(); // updates the previous entry with new values.

Delete Entity

Book book = Book.findById(Book.class, 1);
book.delete();

or

SugarRecord.delete(book); // if using the @Table annotation 

Update Entity based on Unique values

Book book = new Book("isbn123", "Title here", "2nd edition")
book.save();

// Update book with isbn123
Book sameBook = new Book("isbn123", "New Title", "5th edition")
sameBook.update();

book.getId() == sameBook.getId(); // true

or

SugarRecord.update(sameBook); // if using the @Table annotation 

Bulk Insert

List<Book> books = new ArrayList<>();
books.add(new Book("isbn123", "Title here", "2nd edition"))
books.add(new Book("isbn456", "Title here 2", "3nd edition"))
books.add(new Book("isbn789", "Title here 3", "4nd edition"))
SugarRecord.saveInTx(books);

When using ProGuard

# Ensures entities remain un-obfuscated so table and columns are named correctly
-keep class com.yourpackage.yourapp.domainclasspackage.** { *; }

Known Issues.

1. Instant Run.

Instant-Run seems to prevent Sugar ORM from finding the "table" classes, therefore it cannot create the DB tables if you run the app for the first time

When running your app for the first time Turn off Instant run once to allow for the DB tables to be created You can enable it after the tables have been created.

To disable Instant-Run in Android Studio:

(Preferences (Mac) or Settings (PC) -> Build, Execution, Deployment -> Instant Run -> Untick "Enable Instant Run..." )

CHANGELOG

Contributing

Please fork this repository and contribute back using pull requests. Features can be requested using issues. All code, comments, and critiques are greatly appreciated.

Comments
  • No such table error

    No such table error

    Hi, I am new with Sugar. I have a simple android project with a single record named "Abonado". I have followed the wiki' steps but when I ran the project and I had the error: "android.database.sqlite.SQLiteException: no such table: ABONADO (code 1): , while compiling: SELECT * FROM ABONADO". Then I looked the db file created and it’s true, the table ABONADO does not exit. Then I looked the master project SugarActivity and it does not make the tables either and I also had another similar error: "android.database.sqlite.SQLiteException: no such table: TEXT_NOTE (code 1): , while compiling: DELETE FROM TEXT_NOTE". Have you experienced this issue before?Could you please help me? Thanks in advanced. Respectfully.
    ohh I am using android 4.2

    opened by amaurymuro 74
  • No such table exceptions

    No such table exceptions

    Even though I've explicitly called the .save() method on my subclass of SugarRecord (Exercise) when the database is created I'm getting errors when querying and there's no such table exceptions associated with the class.

    bug documentation 
    opened by mrshll1001 48
  • No such table !!

    No such table !!

    hi i tried a lot to make sugar database work but it keeps returning NO SUCH TABLE error here is my code:

    manifest: `

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data android:name="DATABASE" android:value="shd.db" />
        <meta-data android:name="VERSION" android:value="1" />
        <meta-data android:name="QUERY_LOG" android:value="true" />
        <meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="he.hrm.test48" />
    </application>
    

    `

    book class:

    `public class Book extends SugarRecord { @Unique String isbn; String title; String edition;

    // Default constructor is necessary for SugarRecord
    public Book() {
    
    }
    
    public Book(String isbn, String title, String edition) {
        this.isbn = isbn;
        this.title = title;
        this.edition = edition;
    }
    

    }`

    MainActivity

    `public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SugarContext.init(getApplicationContext());
    }
    
    public void gogo(View view){
        Book book = new Book("bbeb","cecf","tegdt");
        book.save();
    }
    

    }`

    it doesn't work at all and the exception is "no such table: BOOK (code 1): , while compiling: INSERT OR REPLACE INTO BOOK(TITLE,ISBN,ID,EDITION) VALUES (?,?,?,?)"

    opened by hoseinhamzei 34
  • Why does SugarORM contain the default ic_launcher.png icons? They are merged with my own ic_launcher.png and I don't want that

    Why does SugarORM contain the default ic_launcher.png icons? They are merged with my own ic_launcher.png and I don't want that

    Some background:

    Using SugarORM in my app and currently I have designed a single app launcher icon that I have named "ic_launcher.png" and put in my app's /drawable folder. I'd like to use that icon globally throughout my app for all device resolutions so I removed ic_launcher.png from the other drawable folders.

    However, SugarORM also includes the default ic_launcher.png in all of it's drawable folders and since I am using SugarORM in the <application> tag, those are merged with my designed icon and device resolutions for which I have not provided ic_launcher use the one coming from the SugarORM folder.

    One way to overcome this is to rename my custom designed icon something different than ic_launcher and put the new name in android:icon but I think most people that use SugarORM will just go for the default configuration and not even notice the merged ic_launcher drawable.

    I think that in the future we should either remove this launcher icon from the SugarORM library or rename or just leave it in the default /drawable folder (no drawable-hdpi, etc).

    opened by Dzhuneyt 28
  • no such table in [com.android.tools.build:gradle:2.0.0-beta6]

    no such table in [com.android.tools.build:gradle:2.0.0-beta6]

    I found in [com.android.tools.build:gradle:2.0.0-beta6] always get No such table error... I trace the source to here class : com.orm.util.ReflectionUtil getAllClasses method always return 0 length Could you fix this bug?

    opened by s90304a123 25
  • No such table exeption

    No such table exeption

    Sugar orm works perfectly on Android <5, but on Android 5> it crashes. I am using version 1.4

    Please help me.

    Error: android.database.sqlite.SQLiteException: no such table: AUDIO (code 1): , while compiling: SELECT * FROM AUDIO

    #Sugar
    -keep class me.lobanov.mp3downloadsfree.models.** { *; }
    
    package me.lobanov.mp3downloadsfree.models;
    
    import com.orm.SugarRecord;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.ToString;
    
    @Getter
    @Setter
    @ToString
    public class Audio extends SugarRecord {
        private long aud_id;
        private String aud_artist;
        private String aud_title;
        private String aud_url;
        private long aud_duration;
    
        public Audio() {
        }
    
        public Audio(long aud_id, String aud_artist, String aud_title, String aud_url, long aud_duration){
            this.aud_id = aud_id;
            this.aud_artist = aud_artist;
            this.aud_title = aud_title;
            this.aud_url = aud_url;
            this.aud_duration = aud_duration;
        }
    }
    
    
    public class App extends SugarApp {
        @Override
        public void onCreate() {
            super.onCreate();
        }
    }
    
    <meta-data android:name="DATABASE" android:value="mp3downloadsfree.db" />
            <meta-data android:name="VERSION" android:value="5" />
            <meta-data android:name="QUERY_LOG" android:value="true" />
            <meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="me.lobanov.mp3downloadsfree.models" />
    
    opened by iqorlobanov 23
  • Not creating tables in Android Studio 2.0 beta 6

    Not creating tables in Android Studio 2.0 beta 6

    I noticed today that after upgrading from Android Studio 2.0 beta 4 to beta 6, that SugarRecord tables are not being created through fresh app install, database migration through version change, database migration through database name change and all other methods.

    I believe this is to do with the 'Instant Run' feature as disabling this seems to resolve the issue.

    I am not sure if there is anything that can be done by Sugar resolve this, but as it is likely to crop up in future, I believe it should be added to the README or associated documentation to warn others.

    opened by ed-george 22
  • ICUCompatApi23 addLikelySubtags Missing

    ICUCompatApi23 addLikelySubtags Missing

    When running sugar 1.3.1 in basic android app with only support library dependencies on device, or emulator, running Jellybean 4.2.2 get the error shown below. No issue running on Lollipop. Looks like a support library issue - opened as ticket against Android. More details on reproduction steps in ticket 188047

    Caused by: java.lang.NoSuchMethodException: addLikelySubtags [class java.util.Locale] at java.lang.Class.getConstructorOrMethod(Class.java:460) at java.lang.Class.getMethod(Class.java:915) at android.support.v4.text.ICUCompatApi23.(ICUCompatApi23.java:35)             at java.lang.Class.classForName(Native Method)             at java.lang.Class.forName(Class.java:217)             at com.orm.SugarDb.getDomainClass(SugarDb.java:60)             at com.orm.SugarDb.getDomainClasses(SugarDb.java:41)             at com.orm.SugarDb.createDatabase(SugarDb.java:104)             at com.orm.SugarDb.onCreate(SugarDb.java:100)             at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:252)             at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)             at com.orm.Database.getDB(Database.java:18)             at com.orm.SugarRecord.save(SugarRecord.java:45)

    opened by richard-instil 20
  • Sugar ORM is not saving data into the database

    Sugar ORM is not saving data into the database

    am currently using Sugar ORM and Android Async Http Client for my Android application.

    I read through the documentation of Sugar ORM and did exactly what is written there. My HttpClient is using the singleton pattern and provides methods for calling some APIs.

    Now comes the bad part about it. I am not able to save the data persistently into my database which is created by Sugar ORM. Here is the method, that is calling an API:

      public void getAvailableMarkets(final Context context, final MarketAdapter adapter) {
        String url = BASE_URL.concat("/markets.json");
        client.addHeader("Content-Type", "application/json");
        client.addHeader("Accept", "application/json");
    
        client.get(context, url, null, new JsonHttpResponseHandler() {
    
            @Override
            public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
    
                Log.i(TAG, "Fetched available markets from server: " + response.toString());
    
    
                Result<Markets> productResult = new Result<Markets>();
                productResult.setResults(new Gson().<ArrayList<Markets>>fromJson(response.toString(),
                        new TypeToken<ArrayList<Markets>>() {
                        }.getType()));
                ArrayList<Markets> marketsArrayList = productResult.getResults();
    
                // This lines tells me that there are no entries in the database
                List<Markets> marketsInDb = Markets.listAll(Markets.class);
    
                if(marketsInDb.size() < marketsArrayList.size() ||
                        marketsInDb.size() > marketsArrayList.size()) {
    
                    Markets.deleteAll(Markets.class);
    
                    for(Markets m : marketsArrayList) {
                        Markets market = new Markets(m.getId(), m.getName(), m.getChainId(), m.getLat(),
                                m.getLng(), m.getBusinessHourId(), m.getCountry(), m.getZip(), m.getCity(),
                                m.getStreet(), m.getPhoto(), m.getIcon(), m.getUrl());
    
                        market.save();
    
                        adapter.add(market);
                    }
    
                    adapter.notifyDataSetChanged();
    
                } 
    
                List<Markets> market = Markets.listAll(Markets.class);
                // This lines proves that Sugar ORM is not saving the entries   
                Log.i(TAG, "The market database list has the size of:" + market.size());
            }
        });
    }
    

    his is what Logcat is printing:

    D/Sugar: Fetching properties I/Sugar: Markets saved : 3 I/Sugar: Markets saved : 5 I/RestClient: The market database list has the size of:0

    Also I took a look at the Sugar ORM tag at stackoverflow, but no answers or questions could give me a hint on how to solve that problem. I am a newbie to the android ecosystem and would love any help of you guys to solve this problem. Thanks in advance

    opened by mehrad-rafigh 20
  • No Such Table A

    No Such Table A

    Sugar orm works perfectly for me when running and installing the android apk from my pc on my phone but it crashes when after being published on the play store. I re-checked the apk severally but it just doesn't work on publish mode. I am using version 1.3 and I have an empty constructor like this :: public Artisan(){ super(); } and this is the error output I get from play store :: Caused by: android.database.sqlite.SQLiteException: no such table: A: , while compiling: SELECT * FROM A WHERE artisan_id = ?

    I have my manifest like this :: where the model class is Artisan but it returns it as "A" as seen above in the query. Thanks.

    opened by saopayne 20
  • Saving an Image using Sugar ORM

    Saving an Image using Sugar ORM

    Hello!

    So, I am trying to save an Image using sugarORM. I first turn it into a ByteArray, and save the byteArray, and then when loading, I turn that byteArray back into a BitMap.

    Now, All the other values I am saving works fine, and I have also tried to display the image by first making it into a byte array and then turning it back into a Bitmap and then deisplaying it. This works fine. The problem arises when I to load.

    After loading, the byteArray gets a value, but after turning it back into a bitMap and diplaying it, it just shows a white image, and the BitMap has the value null. So something goes wrong during the conversion from byteArray to Bitmap I guess? Since the bytearray has SOME VALUE after loading, I do not know 100% certain that this is the same value as I saved, but I cant see any reason why it would change :S

    This is the code I use for saving and loading :

    public void saveImage(){
        imageToSave = Utilities.getBytes(backGround);
    }
    
    public void updateImage(){
        backGround = Utilities.getImage(imageToSave);
    }
    

    This is the Utilities class:

    public class Utilities {
    
    public static byte[] getBytes(Bitmap bitmap) {
        ByteArrayOutputStream stream=new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG,100, stream);
        return stream.toByteArray();
    }
    
    public static Bitmap getImage(byte[] image)
    {
        return BitmapFactory.decodeByteArray(image, 0, image.length);
    }
    
    }
    

    imageToSave is a variable inside the TrackedEvent class. The TrackedEventObject is saved from the MainActivityClass on line 439, doing this: newEvent.saveImage(); newEvent.save();

    where newEvent is an Object of the type TrackedEvent.

    The rest of the code is available here: https://github.com/smholsen/BigDayCountdown/tree/master/app/src/main/java/com/simonm/bigdaycountdown

    Any idea on what I am doing wrong?

    bug tests Needed question 
    opened by smholsen 19
  • no such column when select

    no such column when select

    could use this func " public static List listAll(Class type, String orderBy) " Caused by: android.database.sqlite.SQLiteException: no such column: updateTime (code 1): , while compiling: SELECT * FROM DATA ORDER BY updateTime desc

    I have been define it,but haven't find this column,but the other could work,such as: dataid.

    here is my class: public class Data extends SugarRecord { @Unique String dataid; String userid; String name; String phone; String age; String isTest; String creatTime; String updateTime; public Data(){} }

    opened by hensonwells 0
  • RuntimeException on start

    RuntimeException on start

    Sugar is crashing in just one Android 11 device:

    Fatal Exception: java.lang.RuntimeException
    Unable to start activity ComponentInfo{com.package.company/com.package.company.MainActivity}: android.database.sqlite.SQLiteException: not an error (code 0 SQLITE_OK[0])
    android.app.ActivityThread.performLaunchActivity
    android.database.sqlite.SQLiteConnection.nativeRegisterLocalizedCollators (SQLiteConnection.java)
    android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration (SQLiteConnection.java:647)
    android.database.sqlite.SQLiteConnection.open (SQLiteConnection.java:387)
    android.database.sqlite.SQLiteConnection.open (SQLiteConnection.java:226)
    android.database.sqlite.SQLiteConnectionPool.openConnectionLocked (SQLiteConnectionPool.java:737)
    android.database.sqlite.SQLiteConnectionPool.open (SQLiteConnectionPool.java:284)
    android.database.sqlite.SQLiteConnectionPool.open (SQLiteConnectionPool.java:251)
    android.database.sqlite.SQLiteDatabase.openInner (SQLiteDatabase.java:1392)
    android.database.sqlite.SQLiteDatabase.open (SQLiteDatabase.java:1337)
    android.database.sqlite.SQLiteDatabase.openDatabase (SQLiteDatabase.java:980)
    android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked (SQLiteOpenHelper.java:444)
    android.database.sqlite.SQLiteOpenHelper.getWritableDatabase (SQLiteOpenHelper.java:387)
    com.orm.SugarDb.getDB (SugarDb.java:38)
    com.orm.SugarRecord.getSugarDataBase (SugarRecord.java:35)
    com.orm.SugarRecord.find (SugarRecord.java:201)
    com.orm.SugarRecord.findById (SugarRecord.java:135)
    

    It is the first time I am seeing this issue with this library and I haven't been able to find anyone with the same crash. Even with a clean install, the issue persists.

    Any idea of what might be going on?

    opened by JuanEsporrin 0
  • *.db file is empty, but in application all the lines are shown.

    *.db file is empty, but in application all the lines are shown.

    My *.db file are empty in device explorer, but the application displays all the previously added raws and *.db-wal file is growing. I can't understand where is my db data, if it's displays in application. There is the full project: https://github.com/apptech44/sugar-orm-library-crud-app

    image image

    opened by Reklid 0
  • bulk data not inserted completely

    bulk data not inserted completely

    i am getting data from server and save into database when i get data from server there are 3855 record but after insertion it show 2378 records 1447 is not saved so i do i resolve this issue

    opened by Meerz 0
Releases(v1.5)
An Android helper class to manage database creation and version management using an application's raw asset files

THIS PROJECT IS NO LONGER MAINTAINED Android SQLiteAssetHelper An Android helper class to manage database creation and version management using an app

Jeff Gilfelt 2.2k Jan 7, 2023
A key-value database for Android

SnappyDB SnappyDB is a key-value database for Android it's an alternative for SQLite if you want to use a NoSQL approach. It allows you to store and g

Nabil Hachicha 1.8k Dec 28, 2022
Android Database Performance Benchmarks

ℹ Check out our new performance test app that includes ObjectBox. Android Database Performance Benchmark This project evaluates Android databases and

Markus Junginger 80 Nov 25, 2022
Kodein-DB is a Kotlin/Multiplatform embedded NoSQL database that works on JVM, Android, Kotlin/Native and iOS.

Kodein-DB is a Kotlin/Multiplatform embedded NoSQL database that works on JVM, Android, Kotlin/Native and iOS. It is suited for client or mobile applications.

null 277 Dec 3, 2022
Test any type of cloud database on Android apps. No need of a dedicated backend.

Test any type of cloud database on Android apps. No need of a dedicated backend.

Arjun 9 May 9, 2022
Android with Real-time Database

Android with Real-time Database It was too much effort to build my own real-time database, but the result really satisfying, so it was worth it. Note

null 4 Jun 14, 2022
Realm is a mobile database: a replacement for SQLite & ORMs

Realm is a mobile database that runs directly inside phones, tablets or wearables. This repository holds the source code for the Java version of Realm

Realm 11.4k Jan 9, 2023
ObjectBox is a superfast lightweight database for objects

ObjectBox Java (Kotlin, Android) ObjectBox is a superfast object-oriented database with strong relation support. ObjectBox is embedded into your Andro

ObjectBox 4.1k Dec 30, 2022
A helper library to help using Room with existing pre-populated database [].

Room now supports using a pre-packaged database out of the box, since version 2.2.0 https://developer.android.com/jetpack/androidx/releases/room#2.2.0

Ibrahim Eid 136 Nov 29, 2022
A library for reading Shared Preferences and Database values within the application.

AppDataReader A library for reading and writing Shared Preferences and Database values of the application within the device. Advantages of using this

Anshul Jain 124 Nov 25, 2022
Android library for viewing and sharing in app databases.

DbInspector DbInspector provides a simple way to view the contents of the in-app database for debugging purposes. There is no need to pull the databas

Infinum 925 Dec 17, 2022
A small library to help with Realm.IO integration in Android apps

Android Realm Asset Helper A small library of methods to help with Realm.IO integration in Android apps Copies a realm database from a the assets fold

Quality Mobile Puzzle Apps 29 Dec 28, 2021
A wrapper around Android's SQLiteDatabase with restoring capability

Restorable SQLiteDatabase RestorableSQLiteDatabase is a wrapper to replicate android's SQLiteDatabase class with restoring capability. This wrapper ma

Navid 21 Oct 21, 2022
A simple NoSQL client for Android. Meant as a document store using key/value pairs and some rudimentary querying. Useful for avoiding the hassle of SQL code.

SimpleNoSQL A simple NoSQL client for Android. If you ever wanted to just save some data but didn't really want to worry about where it was going to b

Colin Miller 389 Sep 25, 2022
an android library for debugging what we care about directly in app.

EN | 中文 Pandora is a tool box that allows you to inspect and modify what includes networks, databases, UIs, etc. directly in your application. It is s

linjiang 1.5k Dec 30, 2022
Core Data for Android

NexusData Core Data for Android NexusData is an object graph and persistence framework for Android. It allows for organizing and managing relational d

Dia Kharrat 71 Nov 11, 2022
🧬 Android DataBinding kit for notifying data changes from Model layers to UI layers on MVVM architecture.

?? Android DataBinding kit for notifying data changes from Model layers to UI layers on MVVM architecture.

Jaewoong Eum 276 Jan 5, 2023
Pref-DB - Android SharedPreferences alternative library

Pref-DB Android SharedPreferences alternative library.

M.Fakhri 5 Sep 14, 2022
Insanely easy way to work with Android Database.

Sugar ORM Insanely easy way to work with Android databases. Official documentation can be found here - Check some examples below. The example applicat

null 2.6k Dec 16, 2022
Insanely easy way to work with Android Database.

Sugar ORM Insanely easy way to work with Android databases. Official documentation can be found here - Check some examples below. The example applicat

null 2.6k Jan 9, 2023