Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 28 additions & 24 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
# Change Log

## 29.1.0-rc.3

* Added: first background subscription auto-requests `POST_NOTIFICATIONS` (Android 13+) from the visible activity
* Fixed: `onOpen`/`onClose` callbacks fire correctly when joining or leaving the background host
* Fixed: notification taps are recorded after launching the app, so tap-opened screens land on top

## 29.1.0-rc.2

* Added: `getInitialNotification()`, `onNotificationOpened()`, and the `PushNotificationOpened` data class (topic + data)
* Added: `onOpen`/`onClose` callbacks now fire for background push connections
* Added: push auth falls back to the client's cookie-store session
* Fixed: a foreground message with no live callback now still posts a notification
* Updated: notification taps route through an internal `PushOpenActivity`

## 29.1.0-rc.1

* Updated: regenerated with sdk-generator 5.5.0; no SDK code changes

## 29.1.0-rc.0

* Added: optional `method` param on `Client.chunkedUpload()` to set the upload HTTP method
* Added: background push notifications render the server `notification` title, body, and image
* Updated: `Avatars.updatePhoto()` uploads via `PUT`
* Fixed: an exactly-chunk-sized file uploads in a single request
## 30.0.0

* Breaking: requests send `x-appwrite-response-format: 2.5.0`; responses follow the 2.5 shapes
* Breaking: missing response fields map to their documented defaults
* Breaking: `oauth2` `resource` params now take `List<String>` on the authorize, PAR, device and token methods
* Breaking: `Push.subscribe` takes `notifyInForeground` before the callback; Java callers must pass it
* Breaking: removed the `Topic.any()` and `Topic.all()` wildcard builders and `ResolvedTopic`; `Push.subscribe` takes strings or `Topic`
* Added: `notifyInForeground` option on `Push.subscribe`
* Added: `PushOpenActivity` for opening notifications
* Added: `Query` aggregates (`count`, `countDistinct`, `sum`, `avg`, `min`, `max` and more), `groupBy`, `having`, `distinct`, joins, `relationship`, `covers`, `spatialEquals` and `page`
* Added: `Query.builder()` to chain queries; `build()` returns the query list
* Added: `ExecutionTrigger.EMAIL`
* Updated: `Topic.path` rejects levels containing a NUL character
* Fixed: a file exactly `CHUNK_SIZE` bytes long uploads in a single request

## 29.2.0

* Added: `analytics.createEvent` in a new `Analytics` service to send tracking events
* Added: optional `state` on `account.createOAuth2Session` and `account.createOAuth2Token`, returned unchanged on the success and failure URLs
* Added: optional `membershipId` on `teams.createMembership`
* Fixed: `avatars.updatePhoto` sends `PUT`, so uploading a profile photo no longer returns 404
* Fixed: OAuth2 query parameters (`success`, `failure`, `scopes`, `state`) are URL-encoded, so values containing `&`, `+` or `#` survive the redirect

## 29.1.0

* Added: `account` passkey methods `listPasskeys`, `createPasskey`, `getPasskey`, `updatePasskey`, `deletePasskey` and `updatePasskeyVerification`
* Added: `account.createPasskeyToken` and `account.updatePasskeyToken` for passkey sign-in
* Added: `Passkey`, `PasskeyList`, `PasskeyChallenge` and `TokenSecret` models

## 29.0.0

Expand Down
67 changes: 40 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

![Maven Central](https://img.shields.io/maven-central/v/io.appwrite/sdk-for-android.svg?color=green&style=flat-square)
![License](https://img.shields.io/github/license/appwrite/sdk-for-android.svg?style=flat-square)
![Version](https://img.shields.io/badge/api%20version-2.3.0-blue.svg?style=flat-square)
![Version](https://img.shields.io/badge/api%20version-2.5.0-blue.svg?style=flat-square)
[![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator)
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord)

**This SDK targets Appwrite server version 2.3.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-android/releases) when APIs differ.
**This SDK targets Appwrite server version 2.5.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-android/releases) when APIs differ.

Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the Android SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

Expand Down Expand Up @@ -38,7 +38,7 @@ repositories {
Next, add the dependency to your project's `build.gradle(.kts)` file:

```groovy
implementation("io.appwrite:sdk-for-android:29.1.0-rc.3")
implementation("io.appwrite:sdk-for-android:30.0.0")
```

### Maven
Expand All @@ -49,7 +49,7 @@ Add this to your project's `pom.xml` file:
<dependency>
<groupId>io.appwrite</groupId>
<artifactId>sdk-for-android</artifactId>
<version>29.1.0-rc.3</version>
<version>30.0.0</version>
</dependency>
</dependencies>
```
Expand Down Expand Up @@ -79,8 +79,10 @@ app every 15 to 60 seconds to reconnect, and the broker replays what was sent in

While the app is not on screen, each message is also posted as a notification that opens
your launch activity with `Push.EXTRA_TOPIC` and `Push.EXTRA_PAYLOAD` in its extras. It shows the
title, body and image sent with `createPush`, falling back to the subscription's `title` and the
raw payload for other messages. On Android 13 and later it needs the `POST_NOTIFICATIONS`
title, body and image sent with `createPush`, falling back to the subscription's `title`, and to
the raw payload as the body for messages that are not a JSON object. A push with neither a title
nor a body, such as a data-only push, posts no notification; it still reaches your callback or
`PushReceiver`. On Android 13 and later it needs the `POST_NOTIFICATIONS`
permission: the first background subscription asks for it from the visible activity (the one
`Push` is created with, else the next one to resume), or call
`Push.requestNotificationPermission(activity)` to ask yourself. Set its
Expand Down Expand Up @@ -119,6 +121,13 @@ The SDK uses exact alarms on its own whenever the app may schedule them. To allo
nothing to ask, including when the permission is not declared. The SDK never opens these
screens on its own.

If `backgroundRestricted` is true, the user restricted the app's background usage and messages can
wait until the app is opened. Exact alarms don't lift it, and neither does foreground mode: Android
stops a restricted app's foreground service once it leaves the screen. Ask the user to choose
Unrestricted in the app's battery settings (Settings > Apps > your app > Battery); granting the
battery optimisation exemption also lets the SDK's alarms and jobs run, and `bestEffort` then
reflects only the exact-alarm state.

If the user force-stops the app (Settings > Force stop, and on some devices swiping it away from
recents), Android cancels its alarms and jobs: nothing is delivered until the app is opened
again, and the broker then replays what was sent meanwhile.
Expand Down Expand Up @@ -232,7 +241,7 @@ val user = account.create(

### Type Safety with Models

The Appwrite Android SDK provides type safety when working with database documents through generic methods. Methods like `listDocuments`, `getDocument`, and others accept a `nestedType` parameter that allows you to specify your custom model type for full type safety.
The Appwrite Android SDK provides type safety when working with table rows through generic methods. Methods like `listRows`, `getRow`, and others accept a `nestedType` parameter that allows you to specify your custom model type for full type safety.

**Kotlin:**
```kotlin
Expand All @@ -245,16 +254,17 @@ data class Book(
val isCheckedOut: Boolean
)

val databases = Databases(client)
val tablesDB = TablesDB(client)

try {
val documents = databases.listDocuments(
val result = tablesDB.listRows(
databaseId = "your-database-id",
collectionId = "your-collection-id",
tableId = "your-table-id",
nestedType = Book::class.java // Pass in your custom model type
)

for (book in documents.documents) {
for (row in result.rows) {
val book = row.data
Log.d("Appwrite", "Book: ${book.name} by ${book.author}") // Now you have full type safety
}
} catch (e: AppwriteException) {
Expand Down Expand Up @@ -299,21 +309,24 @@ public class Book {
public void setCheckedOut(boolean checkedOut) { isCheckedOut = checkedOut; }
}

Databases databases = new Databases(client);

try {
DocumentList<Book> documents = databases.listDocuments(
"your-database-id",
"your-collection-id",
Book.class // Pass in your custom model type
);

for (Book book : documents.getDocuments()) {
Log.d("Appwrite", "Book: " + book.getName() + " by " + book.getAuthor()); // Now you have full type safety
}
} catch (AppwriteException e) {
Log.e("Appwrite", e.getMessage() != null ? e.getMessage() : "Unknown error");
}
TablesDB tablesDB = new TablesDB(client);

tablesDB.listRows(
"your-database-id",
"your-table-id",
Book.class, // Pass in your custom model type
new CoroutineCallback<>((result, error) -> {
if (error != null) {
Log.e("Appwrite", error.getMessage() != null ? error.getMessage() : "Unknown error");
return;
}

for (Row<Book> row : result.getRows()) {
Book book = row.getData();
Log.d("Appwrite", "Book: " + book.getName() + " by " + book.getAuthor()); // Now you have full type safety
}
})
);
```

**Tip**: You can use the `appwrite types` command to automatically generate model definitions based on your Appwrite database schema. Learn more about [type generation](https://appwrite.io/docs/products/databases/type-generation).
Expand Down Expand Up @@ -363,7 +376,7 @@ The Appwrite Android SDK raises an `AppwriteException` object with `message`, `c
```kotlin
try {
var user = account.create(ID.unique(),"email@example.com","password","Walter O'Brien")
Log.d("Appwrite user", user.toMap())
Log.d("Appwrite user", user.toMap().toString())
} catch(e : AppwriteException) {
e.printStackTrace()
}
Expand Down
1 change: 1 addition & 0 deletions docs/examples/java/account/create-o-auth-2-session.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ account.createOAuth2Session(
"https://example.com", // success (optional)
"https://example.com", // failure (optional)
List.of(), // scopes (optional)
"<STATE>", // state (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
Expand Down
1 change: 1 addition & 0 deletions docs/examples/java/account/create-o-auth-2-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ account.createOAuth2Token(
"https://example.com", // success (optional)
"https://example.com", // failure (optional)
List.of(), // scopes (optional)
"<STATE>", // state (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
Expand Down
22 changes: 22 additions & 0 deletions docs/examples/java/account/create-passkey-token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
```java
import android.util.Log;

import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;

Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID

Account account = new Account(client);

account.createPasskeyToken(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}

Log.d("Appwrite", result.toString());
}));
```
27 changes: 27 additions & 0 deletions docs/examples/java/account/create-passkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
```java
import android.util.Log;

import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;

Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID

Account account = new Account(client);

account.createPasskey(
"<PASSKEY_ID>", // passkeyId (optional)
"<NAME>", // name (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}

Log.d("Appwrite", result.toString());
})
);

```
26 changes: 26 additions & 0 deletions docs/examples/java/account/delete-passkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
```java
import android.util.Log;

import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;

Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID

Account account = new Account(client);

account.deletePasskey(
"<PASSKEY_ID>", // passkeyId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}

Log.d("Appwrite", result.toString());
})
);

```
26 changes: 26 additions & 0 deletions docs/examples/java/account/get-passkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
```java
import android.util.Log;

import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;

Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID

Account account = new Account(client);

account.getPasskey(
"<PASSKEY_ID>", // passkeyId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}

Log.d("Appwrite", result.toString());
})
);

```
27 changes: 27 additions & 0 deletions docs/examples/java/account/list-passkeys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
```java
import android.util.Log;

import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;

Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID

Account account = new Account(client);

account.listPasskeys(
List.of(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}

Log.d("Appwrite", result.toString());
})
);

```
27 changes: 27 additions & 0 deletions docs/examples/java/account/update-passkey-token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
```java
import android.util.Log;

import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;

Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID

Account account = new Account(client);

account.updatePasskeyToken(
"<CHALLENGE_ID>", // challengeId
Map.of("a", "b"), // credential
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}

Log.d("Appwrite", result.toString());
})
);

```
Loading