├── .github
└── ISSUE_TEMPLATE
│ ├── bug.yaml
│ ├── documentation.yaml
│ └── feature.yaml
├── .gitignore
├── .gitpod.yml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── io
│ │ └── appwrite
│ │ └── playgroundforandroid
│ │ ├── Extensions.kt
│ │ ├── MainActivity.kt
│ │ └── PlaygroundViewModel.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── values-night
│ └── themes.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── themes.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── proguard-rules.pro
└── settings.gradle
/.github/ISSUE_TEMPLATE/bug.yaml:
--------------------------------------------------------------------------------
1 | name: "🐛 Bug Report"
2 | description: "Submit a bug report to help us improve"
3 | title: "🐛 Bug Report: "
4 | labels: [bug]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill out our bug report form 🙏
10 | - type: textarea
11 | id: steps-to-reproduce
12 | validations:
13 | required: true
14 | attributes:
15 | label: "👟 Reproduction steps"
16 | description: "How do you trigger this bug? Please walk us through it step by step."
17 | placeholder: "When I ..."
18 | - type: textarea
19 | id: expected-behavior
20 | validations:
21 | required: true
22 | attributes:
23 | label: "👍 Expected behavior"
24 | description: "What did you think would happen?"
25 | placeholder: "It should ..."
26 | - type: textarea
27 | id: actual-behavior
28 | validations:
29 | required: true
30 | attributes:
31 | label: "👎 Actual Behavior"
32 | description: "What did actually happen? Add screenshots, if applicable."
33 | placeholder: "It actually ..."
34 | - type: dropdown
35 | id: appwrite-version
36 | attributes:
37 | label: "🎲 Appwrite version"
38 | description: "What version of Appwrite are you running?"
39 | options:
40 | - Version 0.10.x
41 | - Version 0.9.x
42 | - Version 0.8.x
43 | - Version 0.7.x
44 | - Version 0.6.x
45 | - Different version (specify in environment)
46 | validations:
47 | required: true
48 | - type: dropdown
49 | id: operating-system
50 | attributes:
51 | label: "💻 Operating system"
52 | description: "What OS is your server / device running on?"
53 | options:
54 | - Linux
55 | - MacOS
56 | - Windows
57 | - Something else
58 | validations:
59 | required: true
60 | - type: textarea
61 | id: enviromnemt
62 | validations:
63 | required: false
64 | attributes:
65 | label: "🧱 Your Environment"
66 | description: "Is your environment customized in any way?"
67 | placeholder: "I use Cloudflare for ..."
68 | - type: checkboxes
69 | id: no-duplicate-issues
70 | attributes:
71 | label: "👀 Have you spent some time to check if this issue has been raised before?"
72 | description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
73 | options:
74 | - label: "I checked and didn't find similar issue"
75 | required: true
76 | - type: checkboxes
77 | id: read-code-of-conduct
78 | attributes:
79 | label: "🏢 Have you read the Code of Conduct?"
80 | options:
81 | - label: "I have read the [Code of Conduct](https://github.com/appwrite/appwrite/blob/HEAD/CODE_OF_CONDUCT.md)"
82 | required: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/documentation.yaml:
--------------------------------------------------------------------------------
1 | name: "📚 Documentation"
2 | description: "Report an issue related to documentation"
3 | title: "📚 Documentation: "
4 | labels: [documentation]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to make our documentation better 🙏
10 | - type: textarea
11 | id: issue-description
12 | validations:
13 | required: true
14 | attributes:
15 | label: "💭 Description"
16 | description: "A clear and concise description of what the issue is."
17 | placeholder: "Documentation should not ..."
18 | - type: checkboxes
19 | id: no-duplicate-issues
20 | attributes:
21 | label: "👀 Have you spent some time to check if this issue has been raised before?"
22 | description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
23 | options:
24 | - label: "I checked and didn't find similar issue"
25 | required: true
26 | - type: checkboxes
27 | id: read-code-of-conduct
28 | attributes:
29 | label: "🏢 Have you read the Code of Conduct?"
30 | options:
31 | - label: "I have read the [Code of Conduct](https://github.com/appwrite/appwrite/blob/HEAD/CODE_OF_CONDUCT.md)"
32 | required: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature.yaml:
--------------------------------------------------------------------------------
1 | name: 🚀 Feature
2 | description: "Submit a proposal for a new feature"
3 | title: "🚀 Feature: "
4 | labels: [feature]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill out our feature request form 🙏
10 | - type: textarea
11 | id: feature-description
12 | validations:
13 | required: true
14 | attributes:
15 | label: "🔖 Feature description"
16 | description: "A clear and concise description of what the feature is."
17 | placeholder: "You should add ..."
18 | - type: textarea
19 | id: pitch
20 | validations:
21 | required: true
22 | attributes:
23 | label: "🎤 Pitch"
24 | description: "Please explain why this feature should be implemented and how it would be used. Add examples, if applicable."
25 | placeholder: "In my use-case, ..."
26 | - type: checkboxes
27 | id: no-duplicate-issues
28 | attributes:
29 | label: "👀 Have you spent some time to check if this issue has been raised before?"
30 | description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
31 | options:
32 | - label: "I checked and didn't find similar issue"
33 | required: true
34 | - type: checkboxes
35 | id: read-code-of-conduct
36 | attributes:
37 | label: "🏢 Have you read the Code of Conduct?"
38 | options:
39 | - label: "I have read the [Code of Conduct](https://github.com/appwrite/appwrite/blob/HEAD/CODE_OF_CONDUCT.md)"
40 | required: true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | kotlin-android/build
17 | /.idea
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | # This configuration file was automatically generated by Gitpod.
2 | # Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml)
3 | # and commit this file to your remote git repository to share the goodness with others.
4 |
5 | # Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
6 |
7 | tasks:
8 | - init: ./gradlew build
9 |
10 |
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Appwrite's Android Playground 🎮
2 |
3 | Appwrite playground for Android is a simple way to explore the Appwrite API & Appwrite Android SDK. Use the source code of this page to learn how to use the different Appwrite Android SDK features.
4 |
5 | ## Get Started
6 |
7 | The **app/src/main/java/com/example/playgroundforandroid/PlaygroundViewModel.kt** file contains all the playground examples and source code.
8 |
9 | You can learn how to integrate your Appwrite Android SDK in your project and see different features of the SDK that can be used.
10 |
11 | This playground doesn't include any Appwrite best practices but rather intended to show the most simple examples and use cases of using Appwrite API in your android app.
12 |
13 | ## Contributing
14 |
15 | All code contributions - including those of people having commit access - must go through a pull request and approved by a core developer before being merged. This is to ensure proper review of all the code.
16 |
17 | We truly ❤️ pull requests! If you wish to help, you can learn more about how you can contribute to this project in the [contribution guide](https://github.com/appwrite/appwrite/blob/master/CONTRIBUTING.md).
18 |
19 | ## Security
20 |
21 | For security issues, kindly email us [security@appwrite.io](mailto:security@appwrite.io) instead of posting a public issue in GitHub.
22 |
23 | ## Follow Us
24 |
25 | Join our growing community around the world! Follow us on [Twitter](https://twitter.com/appwrite), [Facebook Page](https://www.facebook.com/appwrite.io), [Facebook Group](https://www.facebook.com/groups/appwrite.developers/) or join our [Discord Server](https://appwrite.io/discord) for more help, ideas and discussions.
26 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-kapt'
5 | }
6 |
7 | android {
8 | namespace "io.appwrite.playgroundforandroid"
9 | compileSdk 34
10 | defaultConfig {
11 | applicationId "io.appwrite.playgroundforandroid"
12 | minSdk 21
13 | targetSdk 34
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_11
28 | targetCompatibility JavaVersion.VERSION_11
29 | }
30 | kotlinOptions {
31 | jvmTarget = '11'
32 | }
33 | buildFeatures {
34 | viewBinding true
35 | }
36 | }
37 |
38 | dependencies {
39 | implementation 'io.appwrite:sdk-for-android:5.1.0'
40 |
41 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
42 | implementation 'androidx.core:core-ktx:1.8.0'
43 | implementation 'androidx.appcompat:appcompat:1.5.0'
44 | implementation 'com.google.android.material:material:1.6.1'
45 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
46 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
47 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/java/io/appwrite/playgroundforandroid/Extensions.kt:
--------------------------------------------------------------------------------
1 | package io.appwrite.playgroundforandroid
2 |
3 | import android.content.ContentResolver
4 | import android.net.Uri
5 | import android.provider.OpenableColumns
6 |
7 | fun ContentResolver.getFileName(fileUri: Uri): String {
8 | var name = ""
9 | val returnCursor = this.query(fileUri, null, null, null, null)
10 | if (returnCursor != null) {
11 | val nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
12 | returnCursor.moveToFirst()
13 | name = returnCursor.getString(nameIndex)
14 | returnCursor.close()
15 | }
16 | return name
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/io/appwrite/playgroundforandroid/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package io.appwrite.playgroundforandroid
2 |
3 | import android.Manifest
4 | import android.content.pm.PackageManager
5 | import android.net.Uri
6 | import android.os.Bundle
7 | import android.widget.TextView
8 | import android.widget.Toast
9 | import androidx.activity.result.contract.ActivityResultContracts
10 | import androidx.appcompat.app.AlertDialog
11 | import androidx.appcompat.app.AppCompatActivity
12 | import androidx.core.content.ContextCompat
13 | import androidx.lifecycle.ViewModelProvider
14 | import io.appwrite.playgroundforandroid.databinding.ActivityMainBinding
15 | import io.appwrite.enums.OAuthProvider
16 | import java.io.File
17 |
18 | class MainActivity : AppCompatActivity() {
19 |
20 | private lateinit var binding: ActivityMainBinding
21 | private lateinit var viewModel: PlaygroundViewModel
22 |
23 | private val output: TextView by lazy {
24 | binding.textView
25 | }
26 |
27 | private val getContent =
28 | registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
29 | uri ?: return@registerForActivityResult
30 | val descriptor = contentResolver.openFileDescriptor(uri, "r", null)
31 | val copyDest = File(cacheDir, contentResolver.getFileName(uri))
32 | viewModel.uploadFile(descriptor!!.fileDescriptor, copyDest)
33 | }
34 |
35 | private val requestPermissions =
36 | registerForActivityResult(ActivityResultContracts.RequestPermission()) {
37 | if (it) {
38 | getContent.launch("image/*")
39 | }
40 | }
41 |
42 | override fun onCreate(savedInstanceState: Bundle?) {
43 | super.onCreate(savedInstanceState)
44 | binding = ActivityMainBinding.inflate(layoutInflater)
45 | setContentView(binding.root)
46 |
47 | viewModel = ViewModelProvider(this).get(PlaygroundViewModel::class.java)
48 | viewModel.createClient(this)
49 |
50 | setClickListeners()
51 | setObservers()
52 | }
53 |
54 | private fun setClickListeners() {
55 | binding.createAccount.setOnClickListener {
56 | viewModel.createAccount()
57 | }
58 | binding.loginWithEmail.setOnClickListener {
59 | viewModel.createSession()
60 | }
61 | binding.createDoc.setOnClickListener {
62 | viewModel.createDocument()
63 | }
64 | binding.listDoc.setOnClickListener {
65 | viewModel.listDocuments()
66 | }
67 | binding.deleteDoc.setOnClickListener {
68 | viewModel.deleteDocument()
69 | }
70 | binding.createExecution.setOnClickListener {
71 | viewModel.createExecution()
72 | }
73 | binding.listExecutions.setOnClickListener {
74 | viewModel.listExecutions()
75 | }
76 | binding.getExecution.setOnClickListener {
77 | viewModel.getExecution()
78 | }
79 | binding.loginWithFacebook.setOnClickListener {
80 | viewModel.createOAuth2Session(this, OAuthProvider.FACEBOOK)
81 | }
82 | binding.loginWithGithub.setOnClickListener {
83 | viewModel.createOAuth2Session(this, OAuthProvider.GITHUB)
84 | }
85 | binding.loginWithGoogle.setOnClickListener {
86 | viewModel.createOAuth2Session(this, OAuthProvider.GOOGLE)
87 | }
88 | binding.updateEmail.setOnClickListener {
89 | viewModel.updateAccountEmail()
90 | }
91 | binding.updatePrefs.setOnClickListener {
92 | viewModel.updateAccountPrefs()
93 | }
94 | binding.updateStatus.setOnClickListener {
95 | viewModel.updateStatus()
96 | }
97 | binding.subscribeButton.setOnClickListener {
98 | viewModel.subscribeToRealtime()
99 | Toast.makeText(this, R.string.subscribed, Toast.LENGTH_SHORT).show()
100 | }
101 | binding.logoutButton.setOnClickListener {
102 | viewModel.deleteSession()
103 | }
104 |
105 | binding.uploadFile.setOnClickListener {
106 | when (PackageManager.PERMISSION_GRANTED) {
107 | ContextCompat.checkSelfPermission(
108 | this,
109 | Manifest.permission.READ_MEDIA_IMAGES,
110 |
111 | ) -> {
112 | getContent.launch("image/*")
113 | }
114 | else -> {
115 | requestPermissions.launch(Manifest.permission.READ_MEDIA_IMAGES)
116 | }
117 | }
118 | }
119 | binding.listFiles.setOnClickListener {
120 | viewModel.listFiles()
121 | }
122 | binding.deleteFile.setOnClickListener {
123 | viewModel.deleteFile()
124 | }
125 | }
126 |
127 | private fun setObservers() {
128 | viewModel.user.observe(this) {
129 | if (it != null) {
130 | output.text = "${it.name} ${it.email}"
131 | } else {
132 | output.text = getString(R.string.anonymous)
133 | }
134 | }
135 | viewModel.items.observe(this) {
136 | Toast.makeText(
137 | binding.root.context,
138 | "REALTIME EVENT: $it",
139 | Toast.LENGTH_LONG
140 | ).show()
141 | }
142 | viewModel.dialogText.observe(this) {
143 | if (it == null) {
144 | return@observe
145 | }
146 | AlertDialog.Builder(this)
147 | .setTitle("Info")
148 | .setMessage(it)
149 | .setNeutralButton("OK") { d, _ -> d.dismiss() }
150 | .create()
151 | .show()
152 | }
153 | }
154 | }
--------------------------------------------------------------------------------
/app/src/main/java/io/appwrite/playgroundforandroid/PlaygroundViewModel.kt:
--------------------------------------------------------------------------------
1 | package io.appwrite.playgroundforandroid
2 |
3 | import android.content.Context
4 | import androidx.activity.ComponentActivity
5 | import androidx.lifecycle.LiveData
6 | import androidx.lifecycle.MutableLiveData
7 | import androidx.lifecycle.ViewModel
8 | import androidx.lifecycle.viewModelScope
9 | import io.appwrite.*
10 | import io.appwrite.exceptions.AppwriteException
11 | import io.appwrite.extensions.toJson
12 | import io.appwrite.models.*
13 | import io.appwrite.models.User
14 | import io.appwrite.services.*
15 | import io.appwrite.services.Locale
16 | import kotlinx.coroutines.launch
17 | import java.io.File
18 | import java.io.FileDescriptor
19 | import java.io.FileInputStream
20 | import java.io.FileOutputStream
21 | import java.lang.System.currentTimeMillis
22 | import io.appwrite.models.User as AppwriteAccount
23 | import io.appwrite.models.File as AppwriteFile
24 |
25 | class PlaygroundViewModel : ViewModel() {
26 |
27 | private val endpoint = "YOUR_ENDPOINT"
28 | private val projectId = "YOUR_PROJECT_ID"
29 | private val collectionId = "YOUR_COLLECTION_ID" // Single required 'username' string attribute
30 | private val functionId = "YOUR_FUNCTION_ID"
31 | private val bucketId = "YOUR_BUCKET_ID"
32 | private var databaseId = "YOUR_DATABASE_ID"
33 |
34 | private var documentId = ""
35 | private var executionId = ""
36 | private var fileId = ""
37 |
38 | private lateinit var client: Client
39 |
40 | private lateinit var account: Account
41 | private lateinit var avatars: Avatars
42 | private lateinit var databases: Databases
43 | private lateinit var storage: Storage
44 | private lateinit var functions: Functions
45 | private lateinit var locale: Locale
46 | private lateinit var teams: Teams
47 | private lateinit var realtime: Realtime
48 |
49 | private val _items = MutableLiveData()
50 | val items: LiveData = _items
51 |
52 | fun createClient(context: Context) {
53 | client = Client(context)
54 | .setEndpoint(endpoint)
55 | .setProject(projectId)
56 | .setSelfSigned(true)
57 |
58 | account = Account(client)
59 | avatars = Avatars(client)
60 | databases = Databases(client)
61 | storage = Storage(client)
62 | functions = Functions(client)
63 | locale = Locale(client)
64 | teams = Teams(client)
65 | realtime = Realtime(client)
66 | }
67 |
68 | private val _user = MutableLiveData>?>(null)
69 | val user: LiveData>?> = _user
70 |
71 | private val _dialogText = MutableLiveData(null)
72 | val dialogText: LiveData = _dialogText
73 |
74 | var emailId = currentTimeMillis()
75 |
76 | fun createAccount() {
77 | viewModelScope.launch {
78 | try {
79 | val user: User