diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4218585..c32e821 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,6 +35,10 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL @@ -61,8 +65,8 @@ jobs: # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh + # echo "Run, Build Application using script" + # - run: ./gradlew assembleDebug - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000..ed96868 --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,31 @@ +name: Publish - Release + +on: + release: + types: [released] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout project sources + uses: actions/checkout@v3 + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + - name: Setup local.properties + run: | + cat << EOF >> local.properties + sdk.dir=$ANDROID_HOME + EOF + - name: Gradle build and clean + run: | + ./gradlew clean build + - name: Publish the SDK + run: | + ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.mavenCentralUsername }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.mavenCentralPassword }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.signingInMemoryKey }} + ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.signingInMemoryKeyId }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.signingInMemoryKeyPassword }} \ No newline at end of file diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml new file mode 100644 index 0000000..7cf1c3b --- /dev/null +++ b/.github/workflows/publish-snapshot.yml @@ -0,0 +1,40 @@ +name: Publish - Snapshot + +on: + push: + branches: + - master + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout project sources + uses: actions/checkout@v3 + - name: Check whether the version is a snapshot + run: | + if grep -q "\-SNAPSHOT" ./contentstack/build.gradle + then + : + else + exit 1 + fi + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + - name: Setup local.properties + run: | + cat << EOF >> local.properties + sdk.dir=$ANDROID_HOME + EOF + - name: Build the SDK - Snapshot + run: | + ./gradlew clean build + - name: Publish the SDK - Snapshot + run: | + ./gradlew publishAllPublicationsToMavenCentralRepository + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.mavenCentralUsername }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.mavenCentralPassword }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.signingInMemoryKey }} + ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.signingInMemoryKeyId }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.signingInMemoryKeyPassword }} \ No newline at end of file diff --git a/.github/workflows/sca-scan.yml b/.github/workflows/sca-scan.yml index 9304a2a..e652d79 100644 --- a/.github/workflows/sca-scan.yml +++ b/.github/workflows/sca-scan.yml @@ -1,26 +1,28 @@ name: Source Composition Analysis Scan on: pull_request: - types: [ opened, synchronize, reopened ] + types: [opened, synchronize, reopened] jobs: security: runs-on: ubuntu-latest - steps: - uses: actions/checkout@master - - name: Generate local.properties - run: echo "sdk.dir=${ANDROID_SDK_ROOT}" > local.properties - - name: cat - run: cat local.properties - - name: pwd - run: pwd - - name: env - run: env - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/gradle-jdk14@master + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + - name: Setup local.properties + run: | + cat << EOF >> local.properties + sdk.dir=$ANDROID_HOME + host="${{ secrets.HOST }}" + APIKey="${{ secrets.API_KEY }}" + deliveryToken="${{ secrets.DELIVERY_TOKEN }}" + environment="${{ secrets.ENVIRONMENT }}" + contentType="${{ secrets.CONTENT_TYPE }}" + assetUid="${{ secrets.ASSET_UID }}" + EOF + - uses: snyk/actions/setup@master + - run: snyk test env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - args: --fail-on=all - command: test || ls - diff --git a/.gitignore b/.gitignore index 2b75303..5192629 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ /build /captures .externalNativeBuild +/.idea/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b589d56..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/dictionaries/shaileshmishra.xml b/.idea/dictionaries/shaileshmishra.xml deleted file mode 100644 index 46b9d3e..0000000 --- a/.idea/dictionaries/shaileshmishra.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 15a15b2..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 38c211a..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index 31a4180..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 34d68b5..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index b5890c1..4825a14 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,45 @@ +[![Contentstack](https://www.contentstack.com/docs/static/images/contentstack.png)](https://www.contentstack.com/) -[![Contentstack](https://www.contentstack.com/docs/static/images/contentstack.png)](https://www.contentstack.com/) - -# Contentstack Android Persistence Library - - - -Contentstack provides [Android Persistence Library](https://www.contentstack.com/docs/guide/synchronization/using-realm-persistence-library-with-android-sync-sdk) that lets your application store data on the device's local storage. This helps you build apps that can work offline too. Given below is a detailed guide and helpful resources to get started with our android Persistence Library. - -## Prerequisites - - * [Android Studio](https://developer.android.com/studio/) - * [Java jdk 8](https://www.oracle.com/technetwork/es/java/javase/downloads/jdk8-downloads-2133151.html) - -## Setup and Initialize library - - -### For Realm - -You download below four files from [Repository](https://github.com/contentstack/contentstack-android-persistence/tree/master/app/src/main/java/com/contentstack/persistence) and keep it in your src folder. - * RealmStore * SyncManager * SyncPersistable -* SyncStore - -#### Initialize the library - -To start using the library in your application, you will need to initialize it by providing the stack details: - -```java - Stack stack = Contentstack.stack(context, "apiKey", "accessToken", "environment"); - - //Provide realmInstance using - Realm realmInstance = Realm.getDefaultInstance(); - //Provide realmInstance to RealmStore's constructor like below. - RealmStore realmStore = new RealmStore(realmInstance); - - SyncManager manager = new SyncManager(realmStore, stack); - manager.stackRequest(); - +# Contentstack Android Persistence Library + +Contentstack provides [Android Persistence Library](https://www.contentstack.com/docs/guide/synchronization/using-realm-persistence-library-with-android-sync-sdk) that lets your application store data on the device's local storage. This helps you build apps that can work offline too. Given below is a detailed guide and helpful resources to get started with our android Persistence Library. + +## Prerequisites + +* [Android Studio](https://developer.android.com/studio/) IDE +* [Java JDK 8](https://www.oracle.com/technetwork/es/java/javase/downloads/jdk8-downloads-2133151.html) or above installed in your machine +* [Realm](https://www.mongodb.com/docs/realm/sdk/java/install/#std-label-java-install) should be installed + +## Setup and initialize library + +### To use realm local database + +Download the following four files from [Repository](https://github.com/contentstack/contentstack-android-persistence/tree/master/app/src/main/java/com/contentstack/persistence) and place them in your src folder: + +- `RealmStore` +- `SyncManager` +- `SyncPersistable` +- `SyncStore` +- `Utils` + +### Initialize the library + +You can initialize the Persistence SDK using the code below in your application. + +```java +Stack stack = Contentstack.stack(applicationContext,"apiKey","deliveryToken","environment"); +Realm realmInstance = Realm.getDefaultInstance(); // gets default realm instance +RealmStore realmStore = new RealmStore(realmInstance); ////Provide realmInstance to RealmStore's constructor like below +SyncManager manager = new SyncManager(realmStore,stack); +manager.stackRequest(); ``` -We have created an example app using android Persistence Library that stores data on the device's local storage. [Read the tutorial](https://github.com/contentstack/contentstack-android-persistence-example) to get started with the example app. -### Helpful Links - -- [Android Persistence Library Docs](https://www.contentstack.com/docs/guide/synchronization/using-realm-persistence-library-with-android-sync-sdk) -- [Android Persistence Example App](https://github.com/contentstack/contentstack-android-persistence-example) + +***Note:** Explore our example app built with the Android Persistence SDK, which securely stores data on your Android device's local storage. [Read the tutorial](https://github.com/contentstack/contentstack-android-persistence-example) to begin with the example app.* + +*Please write migrations in case table schema changes are expected.* + +### Helpful Links + +- [Android Persistence Library Docs](https://www.contentstack.com/docs/guide/synchronization/using-realm-persistence-library-with-android-sync-sdk) +- [Android Persistence Example App](https://github.com/contentstack/contentstack-android-persistence-example) - [Content Delivery API Docs](https://contentstack.com/docs/apis/content-delivery-api/) \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index efd9904..77fde14 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,14 +1,17 @@ plugins { id 'com.android.application' id "realm-android" + id "com.vanniktech.maven.publish" version "0.27.0" } +// import com.vanniktech.maven.publish.SonatypeHost + android { - compileSdkVersion 33 + compileSdkVersion 34 namespace 'com.contentstack.sdk.persistence' defaultConfig { minSdkVersion 24 - targetSdkVersion 33 + targetSdkVersion 34 versionCode 1 versionName "0.0.2" } @@ -27,11 +30,10 @@ android { } dependencies { - implementation 'com.contentstack.sdk:android:3.13.0' - implementation 'androidx.appcompat:appcompat:1.0.0' - implementation 'com.google.android.material:material:1.0.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.lifecycle:lifecycle-livedata:2.0.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel:2.0.0' -} - + implementation 'com.contentstack.sdk:android:3.14.0-20240208.095825-3' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.11.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.lifecycle:lifecycle-livedata:2.7.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel:2.7.0' +} \ No newline at end of file diff --git a/app/src/main/java/com/contentstack/sdk/persistence/Home.java b/app/src/main/java/com/contentstack/sdk/persistence/Home.java index d28f69c..6c46733 100644 --- a/app/src/main/java/com/contentstack/sdk/persistence/Home.java +++ b/app/src/main/java/com/contentstack/sdk/persistence/Home.java @@ -6,6 +6,7 @@ import io.realm.annotations.RealmClass; import io.realm.annotations.RealmField; +// This is only for testing support @RealmClass(name = "a") public class Home extends RealmObject { diff --git a/app/src/main/java/com/contentstack/sdk/persistence/MainActivity.java b/app/src/main/java/com/contentstack/sdk/persistence/MainActivity.java index 4ec5320..c2f8c37 100644 --- a/app/src/main/java/com/contentstack/sdk/persistence/MainActivity.java +++ b/app/src/main/java/com/contentstack/sdk/persistence/MainActivity.java @@ -10,6 +10,7 @@ import io.realm.Realm; +// This is only for testing support public class MainActivity extends AppCompatActivity { @Override @@ -24,10 +25,10 @@ protected void onCreate(Bundle savedInstanceState) { private void loadTheSDK() { try { Config config = new Config(); - config.setHost("*************"); + config.setHost("************"); Stack stack = Contentstack.stack(getApplicationContext(), - "*************", "*************", - "*************", config); + "************", "************", + "dev", config); Realm realmInstance = Realm.getDefaultInstance(); diff --git a/app/src/main/java/com/contentstack/sdk/persistence/SyncManager.java b/app/src/main/java/com/contentstack/sdk/persistence/SyncManager.java index 8d84872..93c0109 100644 --- a/app/src/main/java/com/contentstack/sdk/persistence/SyncManager.java +++ b/app/src/main/java/com/contentstack/sdk/persistence/SyncManager.java @@ -32,8 +32,8 @@ public class SyncManager { - final private String TAG = SyncManager.class.getSimpleName(); - final private Logger logger = Logger.getLogger(TAG); + final private static String TAG = SyncManager.class.getSimpleName(); + final private static Logger logger = Logger.getLogger(TAG); final private Stack stackInstance; final private RealmStore realmStoreInstance; final private Realm realmInstance; @@ -55,44 +55,63 @@ public SyncManager(@NonNull RealmStore realmStore, @NonNull Stack stackInstance) } - private void handleError(Error error) { - - if (error.getStatusCode() == 422) { - - if (error.getErrors().containsKey("pagination_token")) { + void makeRandomDelay() { + int minDelayMillis = 1000; // Minimum delay of 1000 milliseconds (1 second) + int maxDelayMillis = 3000; // Maximum delay of 3000 milliseconds (3 seconds) + int delayMillis = minDelayMillis + (int) (Math.random() * (maxDelayMillis - minDelayMillis + 1)); + //It's helpful to introduce a random delay of 1000-3000 milliseconds in the request. + try { + Thread.sleep(delayMillis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Restore interrupted state + throw new RuntimeException("Thread sleep interrupted: " + e.getMessage()); + } + } - deleteToken(null, getPaginationToken()); // Delete pagination token from local DB - stackRequest(); - } else if (error.getErrors().containsKey("sync_token")) { - deleteToken(getSyncToken(), null); // Delete sync token from local DB + private void handleError(Error error) { + int statusCode = error.getStatusCode(); + if (statusCode == 422) { + if (error.getErrors().containsKey("sync")) { + deleteSeqToken(); stackRequest(); } else { - - try { - throw new Exception(); - } catch (Exception e) { - throw new RuntimeException(e.getLocalizedMessage()); - } + throw new RuntimeException("Unprocessable Entity Error: " + error.getErrors()); } + } else if (statusCode == 429) { + handleRateLimitError(); + } + } - } else if (error.getStatusCode() == 429) { - int delayMillis = (int) (1000 + Math.random() * 2000); - try { - Thread.sleep(delayMillis); - } catch (InterruptedException e) { - throw new RuntimeException(e.getLocalizedMessage()); - } - stackRequest(); + private void deleteSeqToken() { + try { + SyncStore ts = new SyncStore(); + ts.setSeqToken(null); // Setting Sequence Token Null + realmStoreInstance.beginWriteTransaction(); + realmStoreInstance.getRealmInstance().insertOrUpdate(ts); + realmStoreInstance.commitWriteTransaction(); + } catch (Exception e) { + Log.e("Error :", e.getLocalizedMessage()); + e.printStackTrace(); + } finally { + realmStoreInstance.closeTransaction(); } } - public void stackRequest() { - if (getSyncToken() != null) { - Log.i(TAG, "Call initiated for Sync Token " + getSyncToken()); - stackInstance.syncToken(getSyncToken(), new SyncResultCallBack() { + private void handleRateLimitError() { + makeRandomDelay(); + stackRequest(); + } + + + public void stackRequest() { + Log.w(TAG, "Sync API request made"); + String sequenceId = getToken(TokenEnum.SEQ); + if (sequenceId != null) { + Log.i(TAG, "seqId " + sequenceId); + stackInstance.seqSync(sequenceId, new SyncResultCallBack() { @Override public void onCompletion(SyncStack syncStack, Error error) { if (error == null) { @@ -103,66 +122,116 @@ public void onCompletion(SyncStack syncStack, Error error) { } } }); + } else { + // When the application is already installed and in use with existing data. + String syncToken = getToken(TokenEnum.SYNC); + String paginateToken = getToken(TokenEnum.PAGINATION); + if (syncToken != null || paginateToken != null) { + appAlreadyInUse(); + } else { + newInitialSync(); + } + } + } - } else if (getPaginationToken() != null) { - Log.i(TAG, "Call initiated for Pagination Token " + getPaginationToken()); - stackInstance.syncPaginationToken(getPaginationToken(), new SyncResultCallBack() { + private void newInitialSync() { + // Whn it's fresh start for Application (new usr) ! + stackInstance.initSeqSync(new SyncResultCallBack() { + @Override + public void onCompletion(SyncStack syncStack, Error error) { + Log.i(TAG, "Init Sync"); + if (error == null) { + logger.log(Level.INFO, syncStack.getJSONResponse().toString()); + parseResponse(syncStack); + } else { + handleError(error); + } + } + }); + } + + private void appAlreadyInUse() { + // To provide support for old sync api + String syncToken = getToken(TokenEnum.SYNC); + String paginateToken = getToken(TokenEnum.PAGINATION); + if (syncToken != null) { + stackInstance.syncToken(syncToken, new SyncResultCallBack() { @Override public void onCompletion(SyncStack syncStack, Error error) { - if (error == null) { - logger.log(Level.INFO, syncStack.getJSONResponse().toString()); - parseResponse(syncStack); - } else { - handleError(error); - } + //old sync api support + // it may have data or may not have, when it does not have any data means everything is up to date + // if it contains some data the get last item and generate seqId based on event timestamp + processStackResponse(syncStack); } }); - } else { - stackInstance.sync(new SyncResultCallBack() { + } else if (paginateToken != null) { + stackInstance.syncPaginationToken(paginateToken, new SyncResultCallBack() { @Override public void onCompletion(SyncStack syncStack, Error error) { - Log.i(TAG, "Initial Sync Call"); - if (error == null) { - logger.log(Level.INFO, syncStack.getJSONResponse().toString()); - parseResponse(syncStack); - } else { - handleError(error); - } + //old sync api support + //It defiantly has some data, calculate last item timestamp and generate the seqId + processStackResponse(syncStack); } }); + } else { + newInitialSync(); } } + private void processStackResponse(SyncStack stackResponse) { + // Calculate item size and find the timestamp of the last event to generate seqId + int size = stackResponse.getCount(); + if (size > 0) { + ArrayList jsonList = stackResponse.getItems(); + JSONObject lastItem = jsonList.get(size - 1); + String timestamp = lastItem.optString("event_at"); - private String getSyncToken() { - SyncStore syncStore = realmInstance.where(SyncStore.class).findFirst(); - if (syncStore != null) { - return realmInstance.where(SyncStore.class).findFirst().getSyncToken(); - } else { - Log.e(TAG, "Sync Token Not Found"); - return null; + // Generate seqId based on the timestamp + String seqId = Utils.generateSeqId(timestamp); // TODO: re-visit the code + Log.w(TAG, "Newly generated seqId: " + seqId); + + // Store sequence id along with sync and pagination tokens + String syncToken = stackResponse.getSyncToken(); + String paginateToken = stackResponse.getPaginationToken(); + storeSequenceId(syncToken, paginateToken, seqId); + + // Initiate another stack request for further processing + stackRequest(); } } - private String getPaginationToken() { + private String getToken(TokenEnum tokenType) { SyncStore syncStore = realmInstance.where(SyncStore.class).findFirst(); if (syncStore != null) { - return realmInstance.where(SyncStore.class).findFirst().getPaginationToken(); + switch (tokenType) { + case SEQ: + return syncStore.getSeqToken(); + case SYNC: + return syncStore.getSyncToken(); + case PAGINATION: + return syncStore.getPaginationToken(); + default: + Log.e(TAG, "Invalid token type"); + return null; + } } else { - Log.e(TAG, "Pagination Token Not Found"); + Log.e(TAG, "SyncStore Not Found"); return null; } } + enum TokenEnum { + SEQ, SYNC, PAGINATION + } private void parseResponse(SyncStack stackResponse) { ArrayList jsonList = stackResponse.getItems(); + String seqToken = stackResponse.getSequentialToken(); String syncToken = stackResponse.getSyncToken(); - String pagiToken = stackResponse.getPaginationToken(); - if (syncToken != null) { - persistsToken(syncToken, pagiToken); - } + String paginateToken = stackResponse.getPaginationToken(); + + storeSequenceId(syncToken, paginateToken, seqToken); jsonList.forEach(this::handleJSON); } @@ -365,43 +434,20 @@ private void reflectionThing(Class MODEL_CLASS) { } - private void persistsToken(String sync_token, String pagination_token) { - Log.e("Tokens :", "Sync Token: " + sync_token + " pagination_token: " + pagination_token); + private void storeSequenceId(String syncToken, String paginationToken, String seqId) { + Log.e(TAG, "SyncToken: " + syncToken); + Log.e(TAG, "PaginationToken: " + paginationToken); + Log.e(TAG, "SeqId: " + seqId); try { realmStoreInstance.beginWriteTransaction(); - realmStoreInstance.getRealmInstance().insertOrUpdate(new SyncStore("token", sync_token, pagination_token)); + realmStoreInstance.getRealmInstance().insertOrUpdate(new SyncStore("seqToken", syncToken, paginationToken, seqId)); realmStoreInstance.commitWriteTransaction(); } catch (Exception e) { - Log.e("Persistence Token :", e.getLocalizedMessage().toString()); + Log.e("Error :", e.getLocalizedMessage()); e.printStackTrace(); } finally { realmStoreInstance.closeTransaction(); } } - private void deleteToken(String syncToken, String paginationToken) { - try { - realmStoreInstance.beginWriteTransaction(); - if (syncToken != null) { - realmStoreInstance.getRealmInstance().insertOrUpdate(new SyncStore("token", null, paginationToken)); - Log.i(TAG, "syncToken deleted"); - } - if (paginationToken != null) { - realmStoreInstance.getRealmInstance().insertOrUpdate(new SyncStore("token", syncToken, null)); - Log.i(TAG, "paginationToken deleted"); - } - if (syncToken != null && paginationToken != null) { - realmStoreInstance.getRealmInstance().insertOrUpdate(new SyncStore("token", null, null)); - Log.i(TAG, "sync and pagination token deleted"); - } - realmStoreInstance.commitWriteTransaction(); - } catch (Exception e) { - Log.e("Persistence Token :", e.getLocalizedMessage()); - e.printStackTrace(); - } finally { - realmStoreInstance.closeTransaction(); - } - } - - } diff --git a/app/src/main/java/com/contentstack/sdk/persistence/SyncStore.java b/app/src/main/java/com/contentstack/sdk/persistence/SyncStore.java index e499ae5..9853d2f 100644 --- a/app/src/main/java/com/contentstack/sdk/persistence/SyncStore.java +++ b/app/src/main/java/com/contentstack/sdk/persistence/SyncStore.java @@ -5,20 +5,22 @@ public class SyncStore extends RealmObject { - @PrimaryKey - private String uniqueId; - private String syncToken; - private String paginationToken; - - public SyncStore() { - } - - public SyncStore(String uniqueId, String syncToken, String paginationToken) { + public SyncStore(String uniqueId, String syncToken, String paginationToken, String seqToken) { this.uniqueId = uniqueId; this.syncToken = syncToken; this.paginationToken = paginationToken; + this.seqToken = seqToken; + } + + public SyncStore() { } + @PrimaryKey + private String uniqueId; + private String syncToken; + private String paginationToken; + private String seqToken; + public String getUniqueId() { return uniqueId; } @@ -31,15 +33,25 @@ public String getSyncToken() { return syncToken; } - public void setSyncToken(String sync_token) { - this.syncToken = sync_token; + public void setSyncToken(String syncToken) { + this.syncToken = syncToken; } public String getPaginationToken() { return paginationToken; } - public void setPaginationToken(String pagination_token) { - this.paginationToken = pagination_token; + public void setPaginationToken(String paginationToken) { + this.paginationToken = paginationToken; + } + + public String getSeqToken() { + return seqToken; + } + + public void setSeqToken(String seqToken) { + this.seqToken = seqToken; } -} + + +} \ No newline at end of file diff --git a/app/src/main/java/com/contentstack/sdk/persistence/Utils.java b/app/src/main/java/com/contentstack/sdk/persistence/Utils.java new file mode 100644 index 0000000..d2fc589 --- /dev/null +++ b/app/src/main/java/com/contentstack/sdk/persistence/Utils.java @@ -0,0 +1,33 @@ +package com.contentstack.sdk.persistence; + +import org.bson.types.ObjectId; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Objects; +import java.util.TimeZone; + +public class Utils { + + static String generateSeqId(String timestamp) { + Objects.requireNonNull(timestamp, "Timestamp Should Not Be Null"); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + Date eventAtDate = null; + try { + eventAtDate = sdf.parse(timestamp); + } catch (ParseException e) { + e.printStackTrace(); + } + + // Convert the Date to a Unix timestamp in milliseconds + long unixTimeInMillis = eventAtDate.getTime(); + + // Create a BSON ObjectId using the Unix timestamp + ObjectId objectId = new ObjectId((int) (unixTimeInMillis / 1000), 0); + String seqId = objectId.toHexString(); + System.out.println(seqId); + return seqId; + } +} diff --git a/build.gradle b/build.gradle index 92bbada..73b680e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,14 +2,13 @@ buildscript { repositories { google() mavenCentral() - } dependencies { - classpath 'com.android.tools.build:gradle:8.2.2' + // classpath 'com.android.tools.build:gradle:8.2.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath 'io.realm:realm-gradle-plugin:10.15.1' } } - allprojects { repositories { google() @@ -17,7 +16,6 @@ allprojects { maven { url =uri("https://oss.sonatype.org/content/repositories/snapshots") } } } - task clean(type: Delete) { delete rootProject.buildDir }