Compare commits
99 Commits
main
...
release/0-
Author | SHA1 | Date | |
---|---|---|---|
|
f25ce87e55 | ||
|
f02fd6046c | ||
|
9b360a7a93 | ||
|
defffb1c20 | ||
|
9098f50987 | ||
|
23da18c499 | ||
|
73612da190 | ||
|
0eb4e5a36c | ||
|
15156bda28 | ||
|
682d094644 | ||
|
e1d7bbcb2f | ||
|
6d2df4ef00 | ||
|
5240249e16 | ||
|
d5ac6dde2b | ||
|
97d3306548 | ||
|
1730e056b5 | ||
|
de84f4192d | ||
|
2b4f433c5f | ||
|
940086d4c8 | ||
|
14f9e48f22 | ||
|
43c7d065bf | ||
|
54a346fc32 | ||
|
cd8010cd1a | ||
|
f0c24fe5ad | ||
|
6e5b06c44b | ||
|
6be14283eb | ||
|
04ff6862e9 | ||
|
1c3f1d1f20 | ||
|
be2f2f50ce | ||
|
2f7db40cc5 | ||
|
9c77d316f4 | ||
|
ee34a38b7c | ||
|
8b33f58330 | ||
|
f0eb782366 | ||
|
8a2745a20a | ||
|
1d6b2a412f | ||
|
bf1fe10865 | ||
|
30eccc5bce | ||
|
d77b95cebe | ||
|
c1f00252fa | ||
|
085958916f | ||
|
1dfab0bd07 | ||
|
543f32516d | ||
|
a1f4b8bf0e | ||
|
3835801a52 | ||
|
de5c31d44b | ||
|
841448cec3 | ||
|
d573005ced | ||
|
c5de11ed74 | ||
|
1db5a1ed98 | ||
|
4a3670a82d | ||
|
a9507debf7 | ||
|
9396ae88b0 | ||
|
5058a2b2ed | ||
|
05747aa413 | ||
|
7ae3e7697c | ||
|
b1c14a278e | ||
|
0750ef29e2 | ||
|
0b7acee9ab | ||
|
293e112aa3 | ||
|
7b4a846866 | ||
|
9a458514a9 | ||
|
e7c5c63a9e | ||
|
4c62d96eed | ||
|
ffb3086db6 | ||
|
99fec87996 | ||
|
5b63a8b65f | ||
|
813eda1b34 | ||
|
2c63b64352 | ||
|
4521a60507 | ||
|
659d06bcdc | ||
|
53f2b97c68 | ||
|
f00ddc38ce | ||
|
1fa1acf468 | ||
|
369780906e | ||
|
67aa3abf27 | ||
|
29f635590b | ||
|
409354b7cc | ||
|
df833b7e9c | ||
|
946155110f | ||
|
db0a39104c | ||
|
8f1ea59930 | ||
|
ca7f746c68 | ||
|
8dd99f93d8 | ||
|
8e9c78a99b | ||
|
49f1827dbf | ||
|
a7faa20c99 | ||
|
e1f5001a3d | ||
|
8ced4f7e1a | ||
|
394528abc8 | ||
|
14bb49c50b | ||
|
cca7791a90 | ||
|
8756d18d1a | ||
|
c5abf0ba0d | ||
|
da8151655e | ||
|
5f2fc71371 | ||
|
32028f654c | ||
|
e8f22ed9b3 | ||
|
c5a4907572 |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,38 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Actual behavior**
|
||||
A clear and concise description of what actually happened.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. Pixel 5]
|
||||
- OS: [e.g. Android 11]
|
||||
|
||||
**App version**
|
||||
|
||||
- [e.g. 0.1.0]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
61
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
name: Bug report
|
||||
description: File a bug
|
||||
labels: ["bug", "triage"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Have you read a contributing guide?
|
||||
description: Please read [CONTRINUTING.md](../blob/main/docs/CONTRIBUTING.md) before proceeding. Check if there are existing [bug reports](https://community.anytype.io/c/bug-reports/l/latest?board=default) and upvote them instead. Follow [this article](https://doc.anytype.io/d/community/community-forum) if you don't know how to join the community.
|
||||
options:
|
||||
- label: I have read CONTRIBUTING.md
|
||||
required: true
|
||||
- label: I have searched the existing issues and didn't find any that were similar
|
||||
required: true
|
||||
- label: I have considered creating a pull request with fixes instead of a bug report and want to proceed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: A concise description of what you're experiencing.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. In this environment...
|
||||
2. With this config...
|
||||
3. Run '...'
|
||||
4. See error...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment
|
||||
description: |
|
||||
For example,
|
||||
- **OS**: macOS 14
|
||||
- **Version**: 0.35.4
|
||||
value: |
|
||||
- OS:
|
||||
- Version:
|
||||
render: markdown
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: |
|
||||
Screenshots? Videos? Links? References? Anything that will give us more context about the issue you are encountering!
|
||||
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
required: false
|
||||
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Contributors Community
|
||||
url: https://github.com/orgs/anyproto/discussions
|
||||
about: A better place for contributing discussions.
|
||||
- name: Anytype Community
|
||||
url: https://community.anytype.io
|
||||
about: Existing feature requests and bug reports from Anytype users.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
38
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
name: Feature request
|
||||
description: Suggest an idea
|
||||
labels: ["enhancement", "triage"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Have you read a contributing guide?
|
||||
description: Please read [CONTRINUTING.md](../blob/main/docs/CONTRIBUTING.md) before proceeding. Check if there are existing [feature requests](https://community.anytype.io/c/feature-requests/l/latest?board=default) and upvote them instead. Follow [this article](https://doc.anytype.io/d/community/community-forum) if you don't know how to join the community.
|
||||
options:
|
||||
- label: I have read CONTRIBUTING.md
|
||||
required: true
|
||||
- label: I have searched the existing requests and didn't find any that were similar
|
||||
required: true
|
||||
- label: I have considered creating a pull request instead and want to proceed
|
||||
required: true
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: Clear and concise description of the problem
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: suggested-solution
|
||||
attributes:
|
||||
label: Suggested solution
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternative
|
||||
attributes:
|
||||
label: Alternative
|
||||
description: Clear and concise description of any alternative solutions or features you've considered.
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Any other context or screenshots about the feature request here.
|
||||
|
57
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
<!-- This template inspired by https://github.com/open-sauced/.github/blob/main/.github/PULL_REQUEST_TEMPLATE.md?plain=1 -->
|
||||
|
||||
---
|
||||
|
||||
- [ ] I understand that contributing to this repository will require me to agree with the [CLA](https://github.com/anyproto/open/blob/main/templates/CLA.md)
|
||||
|
||||
<!-- The bot will prompt you to accept the CLA by replying with a pre-composed message in the comments. If you have already accepted the CLA, you won't need to do it again. -->
|
||||
|
||||
---
|
||||
|
||||
### Description
|
||||
|
||||
<!--
|
||||
Please do not leave this blank
|
||||
This PR [adds/removes/fixes/replaces] the [feature/bug/etc].
|
||||
-->
|
||||
|
||||
### What type of PR is this? (check all applicable)
|
||||
|
||||
- [ ] 🍕 Feature
|
||||
- [ ] 🐛 Bug Fix
|
||||
- [ ] 📝 Documentation Update
|
||||
- [ ] 🎨 Style
|
||||
- [ ] 🧑💻 Code Refactor
|
||||
- [ ] 🔥 Performance Improvements
|
||||
- [ ] ✅ Test
|
||||
- [ ] 🤖 Build
|
||||
- [ ] 🔁 CI
|
||||
|
||||
### Related Tickets & Documents
|
||||
<!--
|
||||
Please provide links to issues, community forum posts, or other sources
|
||||
-->
|
||||
|
||||
### Mobile & Desktop Screenshots/Recordings
|
||||
|
||||
<!-- Visual changes require screenshots -->
|
||||
|
||||
### Added tests?
|
||||
|
||||
- [ ] 👍 yes
|
||||
- [ ] 🙅 no, because they aren't needed
|
||||
- [ ] 🙋 no, because I need help
|
||||
|
||||
### Added to documentation?
|
||||
|
||||
- [ ] 📜 README.md
|
||||
- [ ] 📓 [tech-docs](https://github.com/anyproto/tech-docs)
|
||||
- [ ] 🙅 no documentation needed
|
||||
|
||||
### [optional] Are there any post-deployment tasks we need to perform?
|
||||
|
||||
|
||||
<!--
|
||||
For Work In Progress Pull Requests, please use the Draft PR feature,
|
||||
see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests for further details.
|
||||
-->
|
2
.github/workflows/check.yml
vendored
|
@ -3,7 +3,7 @@ on:
|
|||
pull_request:
|
||||
# add "synchronize" in "types", in order to trigger workflow for pull request commit(s) pushes.
|
||||
types: [ opened ]
|
||||
branches: [ main ]
|
||||
branches: [ main, release/**]
|
||||
name: Run debug unit tests
|
||||
jobs:
|
||||
setup-android:
|
||||
|
|
45
.github/workflows/cla.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
name: "CLA Assistant"
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_target:
|
||||
types: [opened,closed,synchronize]
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
|
||||
jobs:
|
||||
CLAAssistant:
|
||||
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get Org Members
|
||||
id: members
|
||||
uses: octokit/request-action@v2.x
|
||||
with:
|
||||
route: GET /orgs/anyproto/members
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ANY_CLA_TOKEN }}
|
||||
|
||||
- name: Extract Member Logins
|
||||
run: |
|
||||
echo '${{ steps.members.outputs.data }}' | jq -r '.[].login' | tr '\n' ',' > members.txt
|
||||
echo "dependabot" >> members.txt
|
||||
echo "members=$(cat members.txt)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: CLA Assistant
|
||||
uses: contributor-assistant/github-action@v2.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PERSONAL_ACCESS_TOKEN: ${{ secrets.ANY_CLA_TOKEN }}
|
||||
with:
|
||||
custom-notsigned-prcomment: '<br/>Thank you for your pull request, we really appreciate it!<br/><br/>Please sign our [Contributor License Agreement](https://github.com/anyproto/open/blob/main/templates/CLA.md) before we can accept your contribution.<br/>You can sign the CLA by simply commenting on this pull request with the following text.<br/>'
|
||||
path-to-document: "https://github.com/anyproto/open/blob/main/templates/CLA.md"
|
||||
remote-organization-name: "anyproto"
|
||||
remote-repository-name: "cla"
|
||||
branch: "main"
|
||||
path-to-signatures: "signatures.json"
|
||||
allowlist: ${{ env.members }}
|
|
@ -20,7 +20,7 @@ rights to anyone else. These terms do not imply any other licenses not
|
|||
expressly granted in this license.
|
||||
|
||||
If Licensee violates any of these terms, or uses the Software in a way not
|
||||
uthorized under this license, the license granted to Licensee ends
|
||||
authorized under this license, the license granted to Licensee ends
|
||||
immediately.
|
||||
If Licensee makes, or authorizes any other person to make, any written claim
|
||||
that the Software, or any other Any Association Product, infringes or
|
||||
|
|
10
README.md
|
@ -80,11 +80,15 @@ Prerequisite: `brew install jq`
|
|||
3. Make sure to update `middlewareVersion` version in `libs.versions.toml`.
|
||||
|
||||
## Contribution
|
||||
Thank you for your desire to develop Anytype together.
|
||||
Thank you for your desire to develop Anytype together!
|
||||
|
||||
Currently, we're not ready to accept PRs, but we will in the nearest future.
|
||||
❤️ This project and everyone involved in it is governed by the [Code of Conduct](docs/CODE_OF_CONDUCT.md).
|
||||
|
||||
Follow us on [Github](https://github.com/anyproto) and join the [Contributors Community](https://github.com/orgs/anyproto/discussions).
|
||||
🧑💻 Check out our [contributing guide](docs/CONTRIBUTING.md) to learn about asking questions, creating issues, or submitting pull requests.
|
||||
|
||||
🫢 For security findings, please email [security@anytype.io](mailto:security@anytype.io) and refer to our [security guide](docs/SECURITY.md) for more information.
|
||||
|
||||
🤝 Follow us on [Github](https://github.com/anyproto) and join the [Contributors Community](https://github.com/orgs/anyproto/discussions).
|
||||
|
||||
---
|
||||
Made by Any — a Swiss association 🇨🇭
|
||||
|
|
|
@ -148,6 +148,29 @@ object EventsDictionary {
|
|||
const val screenHome = "ScreenHome"
|
||||
const val selectHomeTab = "SelectHomeTab"
|
||||
|
||||
// Onboarding events
|
||||
const val screenOnboarding = "ScreenOnboarding"
|
||||
const val clickOnboarding = "ClickOnboarding"
|
||||
const val clickLogin = "ClickLogin"
|
||||
|
||||
enum class ScreenOnboardingStep(val value: String) {
|
||||
VOID("Void"),
|
||||
PHRASE("Phrase"),
|
||||
SOUL("Soul"),
|
||||
SOUL_CREATING("SoulCreating"),
|
||||
SPACE_CREATING("SpaceCreating")
|
||||
}
|
||||
|
||||
enum class ClickOnboardingButton(val value: String) {
|
||||
SHOW_AND_COPY("ShowAndCopy"),
|
||||
CHECK_LATER("CheckLater")
|
||||
}
|
||||
|
||||
enum class ClickLoginButton(val value: String) {
|
||||
PHRASE("Phrase"),
|
||||
QR("Qr"),
|
||||
}
|
||||
|
||||
// Routes
|
||||
object Routes {
|
||||
const val home = "ScreenHome"
|
||||
|
@ -216,4 +239,5 @@ object EventsPropertiesKey {
|
|||
const val align = "align"
|
||||
const val originalId = "originalId"
|
||||
const val view = "view"
|
||||
const val step = "step"
|
||||
}
|
|
@ -3,7 +3,6 @@ plugins {
|
|||
id "kotlin-android"
|
||||
id "kotlin-kapt"
|
||||
id "androidx.navigation.safeargs.kotlin"
|
||||
id "com.google.firebase.crashlytics"
|
||||
id "com.google.firebase.appdistribution"
|
||||
}
|
||||
|
||||
|
@ -106,7 +105,7 @@ android {
|
|||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion libs.versions.androidxComposeVersion1.get()
|
||||
kotlinCompilerExtensionVersion libs.versions.composeKotlinCompilerVersion.get()
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
@ -207,7 +206,6 @@ dependencies {
|
|||
implementation libs.exoPlayerUi
|
||||
|
||||
implementation libs.amplitude
|
||||
implementation libs.okhttp
|
||||
|
||||
implementation libs.shimmerLayout
|
||||
implementation libs.photoView
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
version.versionMajor=0
|
||||
version.versionMinor=23
|
||||
version.versionPatch=0
|
||||
version.versionPatch=17
|
||||
version.useDatedVersionName=false
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
android:name="com.anytypeio.anytype.app.AndroidApplication"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="@xml/my_backup_rules"
|
||||
android:icon="@mipmap/ic_new_app_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:roundIcon="@mipmap/ic_new_app_launcher_round"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
|
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 16 KiB |
|
@ -24,7 +24,7 @@ class DefaultAppActionManager(val context: Context) : AppActionManager {
|
|||
val shortcut = ShortcutInfoCompat.Builder(context, ACTION_CREATE_NEW_ID)
|
||||
.setShortLabel(label)
|
||||
.setLongLabel(label)
|
||||
.setIcon(IconCompat.createWithResource(context, R.mipmap.ic_new_app_launcher))
|
||||
.setIcon(IconCompat.createWithResource(context, R.mipmap.ic_launcher))
|
||||
.setIntent(
|
||||
Intent(Intent.ACTION_VIEW, null).apply {
|
||||
setClass(context, MainActivity::class.java)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.di.common
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.BuildConfig
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.di.feature.AddFileRelationModule
|
||||
import com.anytypeio.anytype.di.feature.AddObjectRelationModule
|
||||
|
@ -63,12 +64,13 @@ import com.anytypeio.anytype.di.feature.auth.DaggerDeletedAccountComponent
|
|||
import com.anytypeio.anytype.di.feature.cover.UnsplashModule
|
||||
import com.anytypeio.anytype.di.feature.home.DaggerHomeScreenComponent
|
||||
import com.anytypeio.anytype.di.feature.library.DaggerLibraryComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingLoginSetupComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingMnemonicComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingMnemonicLoginComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingSoulCreationAnimComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingSoulCreationComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingStartComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.login.DaggerOnboardingLoginSetupComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.login.DaggerOnboardingMnemonicLoginComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingMnemonicComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingSoulCreationAnimComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingSoulCreationComponent
|
||||
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingVoidComponent
|
||||
import com.anytypeio.anytype.di.feature.relations.DaggerRelationCreateFromLibraryComponent
|
||||
import com.anytypeio.anytype.di.feature.relations.DaggerRelationEditComponent
|
||||
|
@ -101,6 +103,7 @@ import com.anytypeio.anytype.di.main.MainComponent
|
|||
import com.anytypeio.anytype.ui.relations.RelationEditParameters
|
||||
import com.anytypeio.anytype.ui.types.edit.TypeEditParameters
|
||||
import com.anytypeio.anytype.ui.widgets.collection.DaggerCollectionComponent
|
||||
import timber.log.Timber
|
||||
|
||||
class ComponentManager(
|
||||
private val main: MainComponent,
|
||||
|
@ -904,6 +907,12 @@ class ComponentManager(
|
|||
.create(findComponentDependencies())
|
||||
}
|
||||
|
||||
val onboardingComponent = Component {
|
||||
DaggerOnboardingComponent
|
||||
.factory()
|
||||
.create(findComponentDependencies())
|
||||
}
|
||||
|
||||
val onboardingStartComponent = Component {
|
||||
DaggerOnboardingStartComponent
|
||||
.factory()
|
||||
|
@ -957,6 +966,16 @@ class ComponentManager(
|
|||
fun release() {
|
||||
instance = null
|
||||
}
|
||||
|
||||
fun isInitialized() = instance != null
|
||||
|
||||
override fun toString(): String {
|
||||
return if (BuildConfig.DEBUG) {
|
||||
instance?.toString().orEmpty()
|
||||
} else {
|
||||
super.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentMap<T>(private val builder: () -> T) {
|
||||
|
@ -970,6 +989,16 @@ class ComponentManager(
|
|||
fun release(id: String) {
|
||||
map.remove(id)
|
||||
}
|
||||
|
||||
fun isInitialized() = map.isNotEmpty()
|
||||
|
||||
override fun toString(): String {
|
||||
return if (BuildConfig.DEBUG) {
|
||||
map.toString()
|
||||
} else {
|
||||
super.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DependentComponentMap<T>(private val builder: (Id) -> T) {
|
||||
|
@ -983,6 +1012,16 @@ class ComponentManager(
|
|||
fun release(id: Id) {
|
||||
map.remove(id)
|
||||
}
|
||||
|
||||
fun isInitialized() = map.isNotEmpty()
|
||||
|
||||
override fun toString(): String {
|
||||
return if (BuildConfig.DEBUG) {
|
||||
map.toString()
|
||||
} else {
|
||||
super.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentWithParams<out T, in PARAMETER>(private val builder: (PARAMETER) -> T) {
|
||||
|
@ -1001,4 +1040,34 @@ class ComponentManager(
|
|||
private inline fun <reified T : ComponentDependencies> findComponentDependencies(): T {
|
||||
return provider.dependencies[T::class.java] as T
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used for debugging and tracing unreleased components.
|
||||
*/
|
||||
fun logUnreleasedComponents() {
|
||||
if (BuildConfig.DEBUG) {
|
||||
javaClass.declaredFields.forEach { field ->
|
||||
val component = field.get(this)
|
||||
if (component != null) {
|
||||
when (component) {
|
||||
is Component<*> -> {
|
||||
if (component.isInitialized()) {
|
||||
Timber.d("Unreleased component: $component")
|
||||
}
|
||||
}
|
||||
is DependentComponentMap<*> -> {
|
||||
if (component.isInitialized()) {
|
||||
Timber.d("Unreleased component: $component")
|
||||
}
|
||||
}
|
||||
is ComponentMap<*> -> {
|
||||
if (component.isInitialized()) {
|
||||
Timber.d("Unreleased component: $component")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.anytypeio.anytype.di.feature.onboarding
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.presentation.onboarding.OnboardingViewModel
|
||||
import com.anytypeio.anytype.ui.onboarding.OnboardingFragment
|
||||
import dagger.Binds
|
||||
import dagger.Component
|
||||
import dagger.Module
|
||||
|
||||
@Component(
|
||||
dependencies = [OnboardingDependencies::class],
|
||||
modules = [
|
||||
OnboardingModule::class,
|
||||
OnboardingModule.Declarations::class
|
||||
]
|
||||
)
|
||||
@PerScreen
|
||||
interface OnboardingComponent {
|
||||
@Component.Factory
|
||||
interface Builder {
|
||||
fun create(dependencies: OnboardingDependencies): OnboardingComponent
|
||||
}
|
||||
|
||||
fun inject(fragment: OnboardingFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
object OnboardingModule {
|
||||
@Module
|
||||
interface Declarations {
|
||||
@Binds
|
||||
@AuthScreenScope
|
||||
fun bindViewModelFactory(factory: OnboardingViewModel.Factory): ViewModelProvider.Factory
|
||||
}
|
||||
}
|
||||
|
||||
interface OnboardingDependencies : ComponentDependencies {
|
||||
fun analytics(): Analytics
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.di.feature.onboarding
|
||||
package com.anytypeio.anytype.di.feature.onboarding.login
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.CrashReporter
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.di.feature.onboarding
|
||||
package com.anytypeio.anytype.di.feature.onboarding.login
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.di.feature.onboarding
|
||||
package com.anytypeio.anytype.di.feature.onboarding.signup
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.auth.interactor.GetMnemonic
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
|
@ -53,6 +54,7 @@ object OnboardingMnemonicModule {
|
|||
|
||||
interface OnboardingMnemonicDependencies : ComponentDependencies {
|
||||
fun authRepository(): AuthRepository
|
||||
fun analytics(): Analytics
|
||||
}
|
||||
|
||||
@Scope
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.di.feature.onboarding
|
||||
package com.anytypeio.anytype.di.feature.onboarding.signup
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
|
@ -9,8 +9,8 @@ import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
|||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
|
||||
import com.anytypeio.anytype.presentation.onboarding.signup.OnboardingSoulCreationAnimViewModel
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
|
||||
import dagger.Binds
|
||||
import dagger.Component
|
||||
import dagger.Module
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.di.feature.onboarding
|
||||
package com.anytypeio.anytype.di.feature.onboarding.signup
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
|
@ -60,6 +61,7 @@ interface OnboardingSoulCreationDependencies : ComponentDependencies {
|
|||
fun blockRepository(): BlockRepository
|
||||
fun dispatchers(): AppCoroutineDispatchers
|
||||
fun configStorage(): ConfigStorage
|
||||
fun analytics(): Analytics
|
||||
}
|
||||
|
||||
@Scope
|
|
@ -1,6 +1,8 @@
|
|||
package com.anytypeio.anytype.di.feature.onboarding.signup
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.CrashReporter
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.auth.interactor.CreateAccount
|
||||
|
@ -9,6 +11,7 @@ import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
|||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.config.UserSettingsRepository
|
||||
import com.anytypeio.anytype.domain.device.PathProvider
|
||||
import com.anytypeio.anytype.domain.`object`.SetupMobileUseCaseSkip
|
||||
import com.anytypeio.anytype.domain.platform.MetricsProvider
|
||||
|
@ -93,6 +96,7 @@ object OnboardingVoidModule {
|
|||
|
||||
interface OnboardingVoidDependencies : ComponentDependencies {
|
||||
fun authRepository(): AuthRepository
|
||||
fun userSettings(): UserSettingsRepository
|
||||
fun blockRepository(): BlockRepository
|
||||
fun configStorage(): ConfigStorage
|
||||
fun workspaceManager(): WorkspaceManager
|
||||
|
@ -101,4 +105,6 @@ interface OnboardingVoidDependencies : ComponentDependencies {
|
|||
fun pathProvider(): PathProvider
|
||||
fun metricsProvider(): MetricsProvider
|
||||
fun dispatchers(): AppCoroutineDispatchers
|
||||
fun analytics(): Analytics
|
||||
fun crashReporter(): CrashReporter
|
||||
}
|
|
@ -2,21 +2,32 @@ package com.anytypeio.anytype.di.feature.settings
|
|||
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.device.share.debug.DebugSpaceDeviceFileContentSaver
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.debugging.DebugSpaceContentSaver
|
||||
import com.anytypeio.anytype.domain.debugging.DebugSpaceShareDownloader
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
|
||||
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
|
||||
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
|
||||
import com.anytypeio.anytype.providers.DefaultUriFileProvider
|
||||
import com.anytypeio.anytype.ui.settings.MainSettingFragment
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.Subcomponent
|
||||
|
||||
@Subcomponent(modules = [MainSettingsModule::class])
|
||||
@Subcomponent(
|
||||
modules = [
|
||||
MainSettingsModule::class,
|
||||
MainSettingsModule.Bindings::class
|
||||
]
|
||||
)
|
||||
@PerScreen
|
||||
interface MainSettingsSubComponent {
|
||||
|
||||
|
@ -70,13 +81,30 @@ object MainSettingsModule {
|
|||
configStorage: ConfigStorage,
|
||||
urlBuilder: UrlBuilder,
|
||||
setObjectDetails: SetObjectDetails,
|
||||
spaceGradientProvider: SpaceGradientProvider
|
||||
spaceGradientProvider: SpaceGradientProvider,
|
||||
debugSpaceShareDownloader: DebugSpaceShareDownloader
|
||||
): MainSettingsViewModel.Factory = MainSettingsViewModel.Factory(
|
||||
analytics,
|
||||
storelessSubscriptionContainer,
|
||||
configStorage,
|
||||
urlBuilder,
|
||||
setObjectDetails,
|
||||
spaceGradientProvider
|
||||
analytics = analytics,
|
||||
storelessSubscriptionContainer = storelessSubscriptionContainer,
|
||||
configStorage = configStorage,
|
||||
urlBuilder = urlBuilder,
|
||||
setObjectDetails = setObjectDetails,
|
||||
spaceGradientProvider = spaceGradientProvider,
|
||||
debugSpaceShareDownloader = debugSpaceShareDownloader
|
||||
)
|
||||
|
||||
@Module
|
||||
interface Bindings {
|
||||
@PerScreen
|
||||
@Binds
|
||||
fun bindUriFileProvider(
|
||||
defaultProvider: DefaultUriFileProvider
|
||||
): UriFileProvider
|
||||
|
||||
@PerScreen
|
||||
@Binds
|
||||
fun bindSpaceDebugDeviceSharer(
|
||||
saver: DebugSpaceDeviceFileContentSaver
|
||||
): DebugSpaceContentSaver
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package com.anytypeio.anytype.di.feature.settings
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.domain.account.DeleteAccount
|
||||
|
@ -17,13 +16,8 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
|
|||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
|
||||
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
|
||||
import com.anytypeio.anytype.providers.DefaultUriFileProvider
|
||||
import com.anytypeio.anytype.ui.settings.ProfileFragment
|
||||
import com.anytypeio.anytype.ui_settings.account.ProfileViewModel
|
||||
import com.anytypeio.anytype.ui_settings.account.repo.DebugSpaceShareDownloader
|
||||
import com.anytypeio.anytype.ui_settings.account.repo.FileSaver
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.Subcomponent
|
||||
|
@ -49,7 +43,6 @@ object ProfileModule {
|
|||
@PerScreen
|
||||
fun provideViewModelFactory(
|
||||
deleteAccount: DeleteAccount,
|
||||
debugSpaceShareDownloader: DebugSpaceShareDownloader,
|
||||
analytics: Analytics,
|
||||
storelessSubscriptionContainer: StorelessSubscriptionContainer,
|
||||
setObjectDetails: SetObjectDetails,
|
||||
|
@ -59,7 +52,6 @@ object ProfileModule {
|
|||
spaceGradientProvider: SpaceGradientProvider
|
||||
): ProfileViewModel.Factory = ProfileViewModel.Factory(
|
||||
deleteAccount = deleteAccount,
|
||||
debugSpaceShareDownloader = debugSpaceShareDownloader,
|
||||
analytics = analytics,
|
||||
storelessSubscriptionContainer = storelessSubscriptionContainer,
|
||||
setObjectDetails = setObjectDetails,
|
||||
|
@ -80,22 +72,6 @@ object ProfileModule {
|
|||
dispatchers: AppCoroutineDispatchers
|
||||
): DebugSpace = DebugSpace(repo = repo, dispatchers = dispatchers)
|
||||
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideFileSaver(
|
||||
uriFileProvider: UriFileProvider,
|
||||
context: Context,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
): FileSaver = FileSaver(context, uriFileProvider, dispatchers)
|
||||
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun providesDebugSyncShareDownloader(
|
||||
debugSpace: DebugSpace,
|
||||
fileSaver: FileSaver,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
): DebugSpaceShareDownloader = DebugSpaceShareDownloader(debugSpace, fileSaver, dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@PerScreen
|
||||
@Provides
|
||||
|
@ -149,11 +125,6 @@ object ProfileModule {
|
|||
|
||||
@Module
|
||||
interface Bindings {
|
||||
|
||||
@PerScreen
|
||||
@Binds
|
||||
fun bindUriFileProvider(
|
||||
defaultProvider: DefaultUriFileProvider
|
||||
): UriFileProvider
|
||||
// Add bindings if needed
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import androidx.security.crypto.EncryptedSharedPreferences
|
|||
import androidx.security.crypto.MasterKeys
|
||||
import com.anytypeio.anytype.app.DefaultAppActionManager
|
||||
import com.anytypeio.anytype.app.DefaultMetricsProvider
|
||||
import com.anytypeio.anytype.core_utils.tools.ThreadInfo
|
||||
import com.anytypeio.anytype.data.auth.config.DefaultFeaturesConfigProvider
|
||||
import com.anytypeio.anytype.data.auth.repo.AuthCache
|
||||
import com.anytypeio.anytype.data.auth.repo.AuthCacheDataStore
|
||||
|
@ -216,8 +217,15 @@ object DataModule {
|
|||
service: MiddlewareService,
|
||||
factory: MiddlewareFactory,
|
||||
logger: MiddlewareProtobufLogger,
|
||||
protobufConverter: ProtobufConverterProvider
|
||||
): Middleware = Middleware(service, factory, logger, protobufConverter)
|
||||
protobufConverter: ProtobufConverterProvider,
|
||||
threadInfo: ThreadInfo
|
||||
): Middleware = Middleware(
|
||||
service = service,
|
||||
factory = factory,
|
||||
logger = logger,
|
||||
protobufConverter = protobufConverter,
|
||||
threadInfo = threadInfo
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
|
|
|
@ -21,12 +21,13 @@ import com.anytypeio.anytype.di.feature.SplashDependencies
|
|||
import com.anytypeio.anytype.di.feature.auth.DeletedAccountDependencies
|
||||
import com.anytypeio.anytype.di.feature.home.HomeScreenDependencies
|
||||
import com.anytypeio.anytype.di.feature.library.LibraryDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.OnboardingLoginSetupDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.OnboardingMnemonicDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.OnboardingMnemonicLoginDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.OnboardingSoulCreationAnimDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.OnboardingSoulCreationDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.OnboardingDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.OnboardingStartDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.login.OnboardingLoginSetupDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.login.OnboardingMnemonicLoginDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingMnemonicDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingSoulCreationAnimDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingSoulCreationDependencies
|
||||
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingVoidDependencies
|
||||
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromLibraryDependencies
|
||||
import com.anytypeio.anytype.di.feature.relations.RelationEditDependencies
|
||||
|
@ -84,6 +85,7 @@ interface MainComponent :
|
|||
MigrationErrorDependencies,
|
||||
BacklinkOrAddToObjectDependencies,
|
||||
FilesStorageDependencies,
|
||||
OnboardingDependencies,
|
||||
OnboardingStartDependencies,
|
||||
OnboardingVoidDependencies,
|
||||
OnboardingMnemonicDependencies,
|
||||
|
@ -198,6 +200,11 @@ private abstract class ComponentDependenciesModule private constructor() {
|
|||
@ComponentDependenciesKey(FilesStorageDependencies::class)
|
||||
abstract fun provideFilesStorageDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(OnboardingDependencies::class)
|
||||
abstract fun provideOnboardingDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(OnboardingStartDependencies::class)
|
||||
|
|
|
@ -4,12 +4,14 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.anytypeio.anytype.BuildConfig
|
||||
import com.anytypeio.anytype.app.TogglePrefs
|
||||
import com.anytypeio.anytype.app.DefaultFeatureToggles
|
||||
import com.anytypeio.anytype.app.TogglePrefs
|
||||
import com.anytypeio.anytype.core_utils.tools.AppInfo
|
||||
import com.anytypeio.anytype.core_utils.tools.DefaultAppInfo
|
||||
import com.anytypeio.anytype.core_utils.tools.DefaultThreadInfo
|
||||
import com.anytypeio.anytype.core_utils.tools.DefaultUrlValidator
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.core_utils.tools.ThreadInfo
|
||||
import com.anytypeio.anytype.core_utils.tools.UrlValidator
|
||||
import com.anytypeio.anytype.domain.config.Gateway
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -44,6 +46,10 @@ object UtilModule {
|
|||
@Module
|
||||
interface Bindings {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindThreadInfo(info: DefaultThreadInfo): ThreadInfo
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindUrlValidator(applicator: DefaultUrlValidator): UrlValidator
|
||||
|
|
|
@ -7,17 +7,18 @@ import androidx.navigation.navOptions
|
|||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
||||
import com.anytypeio.anytype.presentation.settings.EditorSettings
|
||||
import com.anytypeio.anytype.presentation.widgets.collection.Subscription
|
||||
import com.anytypeio.anytype.ui.auth.Keys
|
||||
import com.anytypeio.anytype.ui.auth.account.CreateAccountFragment.Companion.ARGS_CODE
|
||||
import com.anytypeio.anytype.ui.auth.account.DeletedAccountFragment
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
import com.anytypeio.anytype.ui.home.HomeScreenFragment
|
||||
import com.anytypeio.anytype.ui.search.ObjectSearchFragment
|
||||
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
|
||||
import com.anytypeio.anytype.ui.settings.RemoteStorageFragment
|
||||
import com.anytypeio.anytype.ui.templates.TemplateSelectFragment
|
||||
import com.anytypeio.anytype.ui.widgets.collection.CollectionFragment
|
||||
import timber.log.Timber
|
||||
|
||||
class Navigator : AppNavigation {
|
||||
|
||||
|
@ -40,10 +41,6 @@ class Navigator : AppNavigation {
|
|||
navController?.navigate(R.id.action_global_desktopScreen, bundle)
|
||||
}
|
||||
|
||||
override fun startLogin() {
|
||||
navController?.navigate(R.id.action_splashFragment_to_login_nav)
|
||||
}
|
||||
|
||||
override fun createProfile(invitationCode: String) {
|
||||
val bundle = bundleOf(ARGS_CODE to invitationCode)
|
||||
navController?.navigate(R.id.action_invitationFragment_to_createAccountScreen, bundle)
|
||||
|
@ -76,17 +73,18 @@ class Navigator : AppNavigation {
|
|||
override fun workspace() {}
|
||||
|
||||
override fun openSettings() {
|
||||
navController?.navigate(R.id.action_open_settings)
|
||||
try {
|
||||
navController?.navigate(R.id.action_open_settings)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while opening settings")
|
||||
}
|
||||
}
|
||||
|
||||
override fun openDocument(id: String, editorSettings: EditorSettings?) {
|
||||
override fun openDocument(id: String) {
|
||||
navController?.navigate(
|
||||
R.id.objectNavigation,
|
||||
Bundle().apply {
|
||||
putString(EditorFragment.ID_KEY, id)
|
||||
editorSettings?.let {
|
||||
putParcelable(EditorFragment.DEBUG_SETTINGS, it)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -97,6 +95,9 @@ class Navigator : AppNavigation {
|
|||
bundleOf(EditorFragment.ID_KEY to id),
|
||||
navOptions {
|
||||
launchSingleTop = true
|
||||
popUpTo(R.id.pageSearchFragment) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -114,6 +115,9 @@ class Navigator : AppNavigation {
|
|||
bundleOf(ObjectSetFragment.CONTEXT_ID_KEY to id),
|
||||
navOptions {
|
||||
launchSingleTop = true
|
||||
popUpTo(R.id.pageSearchFragment) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -189,8 +193,11 @@ class Navigator : AppNavigation {
|
|||
navController?.navigate(R.id.action_profileScreen_to_debugSettingsFragment)
|
||||
}
|
||||
|
||||
override fun openPageSearch() {
|
||||
navController?.navigate(R.id.pageSearchFragment)
|
||||
override fun openPageSearch(ctx: Id?) {
|
||||
navController?.navigate(
|
||||
R.id.pageSearchFragment,
|
||||
bundleOf(ObjectSearchFragment.CTX_KEY to ctx)
|
||||
)
|
||||
}
|
||||
|
||||
override fun exitToDesktopAndOpenPage(pageId: String) {
|
||||
|
@ -254,6 +261,14 @@ class Navigator : AppNavigation {
|
|||
navController?.navigate(R.id.migrationNeededScreen)
|
||||
}
|
||||
|
||||
override fun exitFromMigrationScreen() {
|
||||
navController?.navigate(R.id.onboarding_nav, null, navOptions {
|
||||
popUpTo(R.id.migrationNeededScreen) {
|
||||
inclusive = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun openTemplates(
|
||||
ctx: Id,
|
||||
type: String,
|
||||
|
|
|
@ -9,68 +9,80 @@ class NavigationRouter(
|
|||
) {
|
||||
fun navigate(command: AppNavigation.Command) {
|
||||
Timber.d("Navigate to $command")
|
||||
try {
|
||||
when (command) {
|
||||
is AppNavigation.Command.StartSplashFromDesktop -> navigation.startSplashFromDesktop()
|
||||
is AppNavigation.Command.StartDesktopFromLogin -> navigation.startDesktopFromLogin()
|
||||
is AppNavigation.Command.StartDesktopFromSignUp -> navigation.startDesktopFromSignUp()
|
||||
is AppNavigation.Command.StartDesktopFromSplash -> navigation.startDesktopFromSplash()
|
||||
is AppNavigation.Command.ExitFromMigrationScreen -> navigation.exitFromMigrationScreen()
|
||||
is AppNavigation.Command.OpenCreateAccount -> navigation.createProfile(command.invitationCode)
|
||||
is AppNavigation.Command.ChoosePinCodeScreen -> navigation.choosePinCode()
|
||||
is AppNavigation.Command.EnterKeyChainScreen -> navigation.enterKeychain()
|
||||
is AppNavigation.Command.SelectAccountScreen -> navigation.chooseAccount()
|
||||
is AppNavigation.Command.WorkspaceScreen -> navigation.workspace()
|
||||
is AppNavigation.Command.InvitationCodeScreen -> navigation.enterInvitationCode()
|
||||
is AppNavigation.Command.AboutAnalyticsScreen -> navigation.aboutAnalyticsScreen()
|
||||
is AppNavigation.Command.ExitToInvitationCodeScreen -> navigation.exitToInvitationCodeScreen()
|
||||
is AppNavigation.Command.SetupNewAccountScreen -> navigation.setupNewAccount()
|
||||
is AppNavigation.Command.SetupSelectedAccountScreen -> navigation.setupSelectedAccount(
|
||||
command.id
|
||||
)
|
||||
|
||||
when (command) {
|
||||
is AppNavigation.Command.StartSplashFromDesktop -> navigation.startSplashFromDesktop()
|
||||
is AppNavigation.Command.StartDesktopFromLogin -> navigation.startDesktopFromLogin()
|
||||
is AppNavigation.Command.StartDesktopFromSignUp -> navigation.startDesktopFromSignUp()
|
||||
is AppNavigation.Command.StartDesktopFromSplash -> navigation.startDesktopFromSplash()
|
||||
is AppNavigation.Command.OpenStartLoginScreen -> navigation.startLogin()
|
||||
is AppNavigation.Command.OpenCreateAccount -> navigation.createProfile(command.invitationCode)
|
||||
is AppNavigation.Command.ChoosePinCodeScreen -> navigation.choosePinCode()
|
||||
is AppNavigation.Command.EnterKeyChainScreen -> navigation.enterKeychain()
|
||||
is AppNavigation.Command.SelectAccountScreen -> navigation.chooseAccount()
|
||||
is AppNavigation.Command.WorkspaceScreen -> navigation.workspace()
|
||||
is AppNavigation.Command.InvitationCodeScreen -> navigation.enterInvitationCode()
|
||||
is AppNavigation.Command.AboutAnalyticsScreen -> navigation.aboutAnalyticsScreen()
|
||||
is AppNavigation.Command.ExitToInvitationCodeScreen -> navigation.exitToInvitationCodeScreen()
|
||||
is AppNavigation.Command.SetupNewAccountScreen -> navigation.setupNewAccount()
|
||||
is AppNavigation.Command.SetupSelectedAccountScreen -> navigation.setupSelectedAccount(
|
||||
command.id
|
||||
)
|
||||
is AppNavigation.Command.ConfirmPinCodeScreen -> navigation.confirmPinCode(command.code)
|
||||
is AppNavigation.Command.OpenSettings -> navigation.openSettings()
|
||||
is AppNavigation.Command.OpenObject -> navigation.openDocument(
|
||||
command.id,
|
||||
command.editorSettings
|
||||
)
|
||||
is AppNavigation.Command.OpenSetOrCollection -> navigation.openObjectSet(
|
||||
command.target,
|
||||
command.isPopUpToDashboard
|
||||
)
|
||||
is AppNavigation.Command.LaunchObjectSet -> navigation.launchObjectSet(command.target)
|
||||
is AppNavigation.Command.LaunchDocument -> navigation.launchDocument(command.id)
|
||||
is AppNavigation.Command.LaunchObjectFromSplash -> navigation.launchObjectFromSplash(
|
||||
command.target
|
||||
)
|
||||
is AppNavigation.Command.LaunchObjectSetFromSplash -> navigation.launchObjectSetFromSplash(
|
||||
command.target
|
||||
)
|
||||
is AppNavigation.Command.OpenDatabaseViewAddView -> navigation.openDatabaseViewAddView()
|
||||
is AppNavigation.Command.OpenEditDatabase -> navigation.openEditDatabase()
|
||||
is AppNavigation.Command.OpenKeychainScreen -> navigation.openKeychainScreen()
|
||||
is AppNavigation.Command.OpenUserSettingsScreen -> navigation.openUserSettingsScreen()
|
||||
is AppNavigation.Command.OpenContactsScreen -> navigation.openContacts()
|
||||
is AppNavigation.Command.OpenSwitchDisplayView -> navigation.openSwitchDisplayView()
|
||||
is AppNavigation.Command.OpenCustomizeDisplayView -> navigation.openCustomizeDisplayView()
|
||||
is AppNavigation.Command.Exit -> navigation.exit()
|
||||
is AppNavigation.Command.ExitToDesktop -> navigation.exitToDesktop()
|
||||
is AppNavigation.Command.OpenDebugSettingsScreen -> navigation.openDebugSettings()
|
||||
is AppNavigation.Command.ExitToDesktopAndOpenPage -> navigation.exitToDesktopAndOpenPage(
|
||||
command.pageId
|
||||
)
|
||||
is AppNavigation.Command.OpenPageSearch -> navigation.openPageSearch()
|
||||
is AppNavigation.Command.OpenUpdateAppScreen -> navigation.openUpdateAppScreen()
|
||||
is AppNavigation.Command.DeletedAccountScreen -> navigation.deletedAccountScreen(command.deadline)
|
||||
is AppNavigation.Command.OpenTemplates -> navigation.openTemplates(
|
||||
ctx = command.ctx,
|
||||
type = command.type,
|
||||
templates = command.templates
|
||||
)
|
||||
is AppNavigation.Command.OpenLibrary -> navigation.openLibrary()
|
||||
is AppNavigation.Command.MigrationErrorScreen -> navigation.migrationErrorScreen()
|
||||
is AppNavigation.Command.OpenRemoteStorageScreen -> navigation.openRemoteStorageScreen(command.subscription)
|
||||
else -> Timber.d("Nav command ignored: $command")
|
||||
is AppNavigation.Command.ConfirmPinCodeScreen -> navigation.confirmPinCode(command.code)
|
||||
is AppNavigation.Command.OpenSettings -> navigation.openSettings()
|
||||
is AppNavigation.Command.OpenObject -> navigation.openDocument(command.id)
|
||||
is AppNavigation.Command.OpenSetOrCollection -> navigation.openObjectSet(
|
||||
command.target,
|
||||
command.isPopUpToDashboard
|
||||
)
|
||||
|
||||
is AppNavigation.Command.LaunchObjectSet -> navigation.launchObjectSet(command.target)
|
||||
is AppNavigation.Command.LaunchDocument -> navigation.launchDocument(command.id)
|
||||
is AppNavigation.Command.LaunchObjectFromSplash -> navigation.launchObjectFromSplash(
|
||||
command.target
|
||||
)
|
||||
|
||||
is AppNavigation.Command.LaunchObjectSetFromSplash -> navigation.launchObjectSetFromSplash(
|
||||
command.target
|
||||
)
|
||||
|
||||
is AppNavigation.Command.OpenDatabaseViewAddView -> navigation.openDatabaseViewAddView()
|
||||
is AppNavigation.Command.OpenEditDatabase -> navigation.openEditDatabase()
|
||||
is AppNavigation.Command.OpenKeychainScreen -> navigation.openKeychainScreen()
|
||||
is AppNavigation.Command.OpenUserSettingsScreen -> navigation.openUserSettingsScreen()
|
||||
is AppNavigation.Command.OpenContactsScreen -> navigation.openContacts()
|
||||
is AppNavigation.Command.OpenSwitchDisplayView -> navigation.openSwitchDisplayView()
|
||||
is AppNavigation.Command.OpenCustomizeDisplayView -> navigation.openCustomizeDisplayView()
|
||||
is AppNavigation.Command.Exit -> navigation.exit()
|
||||
is AppNavigation.Command.ExitToDesktop -> navigation.exitToDesktop()
|
||||
is AppNavigation.Command.OpenDebugSettingsScreen -> navigation.openDebugSettings()
|
||||
is AppNavigation.Command.ExitToDesktopAndOpenPage -> navigation.exitToDesktopAndOpenPage(
|
||||
command.pageId
|
||||
)
|
||||
|
||||
is AppNavigation.Command.OpenPageSearch -> navigation.openPageSearch(command.ctx)
|
||||
is AppNavigation.Command.OpenUpdateAppScreen -> navigation.openUpdateAppScreen()
|
||||
is AppNavigation.Command.DeletedAccountScreen -> navigation.deletedAccountScreen(
|
||||
command.deadline
|
||||
)
|
||||
|
||||
is AppNavigation.Command.OpenTemplates -> navigation.openTemplates(
|
||||
ctx = command.ctx,
|
||||
type = command.type,
|
||||
templates = command.templates
|
||||
)
|
||||
|
||||
is AppNavigation.Command.OpenLibrary -> navigation.openLibrary()
|
||||
is AppNavigation.Command.MigrationErrorScreen -> navigation.migrationErrorScreen()
|
||||
is AppNavigation.Command.OpenRemoteStorageScreen -> navigation.openRemoteStorageScreen(
|
||||
command.subscription
|
||||
)
|
||||
|
||||
else -> Timber.d("Nav command ignored: $command")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while navigation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ import com.anytypeio.anytype.core_ui.menu.ObjectTypePopupMenu
|
|||
import com.anytypeio.anytype.core_ui.reactive.clicks
|
||||
import com.anytypeio.anytype.core_ui.tools.ClipboardInterceptor
|
||||
import com.anytypeio.anytype.core_ui.tools.EditorHeaderOverlayDetector
|
||||
import com.anytypeio.anytype.core_ui.tools.LastItemBottomOffsetDecorator
|
||||
import com.anytypeio.anytype.core_ui.tools.MarkupColorToolbarFooter
|
||||
import com.anytypeio.anytype.core_ui.tools.MentionFooterItemDecorator
|
||||
import com.anytypeio.anytype.core_ui.tools.NoteHeaderItemDecorator
|
||||
|
@ -85,6 +86,7 @@ import com.anytypeio.anytype.core_utils.ext.gone
|
|||
import com.anytypeio.anytype.core_utils.ext.hide
|
||||
import com.anytypeio.anytype.core_utils.ext.hideSoftInput
|
||||
import com.anytypeio.anytype.core_utils.ext.invisible
|
||||
import com.anytypeio.anytype.core_utils.ext.lastDecorator
|
||||
import com.anytypeio.anytype.core_utils.ext.safeNavigate
|
||||
import com.anytypeio.anytype.core_utils.ext.screen
|
||||
import com.anytypeio.anytype.core_utils.ext.show
|
||||
|
@ -245,6 +247,12 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
)
|
||||
}
|
||||
|
||||
private val defaultBottomOffsetDecorator by lazy {
|
||||
LastItemBottomOffsetDecorator(
|
||||
dimen(R.dimen.dp_48)
|
||||
)
|
||||
}
|
||||
|
||||
private val footerMentionDecorator by lazy { MentionFooterItemDecorator(screen) }
|
||||
private val noteHeaderDecorator by lazy {
|
||||
NoteHeaderItemDecorator(offset = dimen(R.dimen.default_note_title_offset))
|
||||
|
@ -503,6 +511,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
itemAnimator = null
|
||||
adapter = blockAdapter
|
||||
addOnScrollListener(titleVisibilityDetector)
|
||||
addItemDecoration(defaultBottomOffsetDecorator)
|
||||
}
|
||||
|
||||
binding.toolbar.apply {
|
||||
|
@ -1376,7 +1385,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
binding.styleToolbarMain.setSelectedStyle(this.state)
|
||||
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
|
||||
keyboardDelayJobs += lifecycleScope.launch {
|
||||
if (binding.recycler.itemDecorationCount == 0) {
|
||||
if (binding.recycler.lastDecorator() == defaultBottomOffsetDecorator) {
|
||||
binding.recycler.addItemDecoration(styleToolbarFooter)
|
||||
}
|
||||
proceedWithHidingSoftInput()
|
||||
|
@ -1441,7 +1450,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
}
|
||||
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
|
||||
keyboardDelayJobs += lifecycleScope.launch {
|
||||
if (binding.recycler.itemDecorationCount == 0) {
|
||||
if (binding.recycler.lastDecorator() == defaultBottomOffsetDecorator) {
|
||||
binding.recycler.addItemDecoration(styleToolbarFooter)
|
||||
}
|
||||
proceedWithHidingSoftInput()
|
||||
|
@ -1490,11 +1499,6 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
}
|
||||
} else {
|
||||
if (binding.slashWidget.isVisible) {
|
||||
applySlideTransition(
|
||||
binding.slashWidget,
|
||||
SLASH_HIDE_ANIM_DURATION,
|
||||
binding.sheet
|
||||
)
|
||||
binding.slashWidget.gone()
|
||||
}
|
||||
binding.recycler.removeItemDecoration(slashWidgetFooter)
|
||||
|
@ -1538,7 +1542,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
)
|
||||
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
|
||||
keyboardDelayJobs += lifecycleScope.launch {
|
||||
if (binding.recycler.itemDecorationCount == 0) {
|
||||
if (binding.recycler.lastDecorator() == defaultBottomOffsetDecorator) {
|
||||
binding.recycler.addItemDecoration(styleToolbarFooter)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
|
@ -1770,15 +1774,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
}
|
||||
|
||||
private fun enterScrollAndMove() {
|
||||
if (binding.recycler.itemDecorationCount == 0 || binding.recycler.getItemDecorationAt(0) !is ScrollAndMoveTargetHighlighter) {
|
||||
|
||||
// val offset = recycler.computeVerticalScrollOffset()
|
||||
//
|
||||
// lifecycleScope.launch {
|
||||
// recycler.layoutChanges().take(1).collect {
|
||||
// if (offset < screen.y / 3) recycler.scrollBy(0, screen.y / 3)
|
||||
// }
|
||||
// }
|
||||
if (binding.recycler.lastDecorator() !is ScrollAndMoveTargetHighlighter) {
|
||||
|
||||
binding.recycler.addItemDecoration(scrollAndMoveTargetHighlighter)
|
||||
|
||||
|
@ -2100,7 +2096,6 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
|
||||
companion object {
|
||||
const val ID_KEY = "id"
|
||||
const val DEBUG_SETTINGS = "debug_settings"
|
||||
|
||||
const val DEFAULT_ANIM_DURATION = 150L
|
||||
const val DEFAULT_DELAY_BLOCK_ACTION_TOOLBAR = 100L
|
||||
|
|
|
@ -53,23 +53,26 @@ abstract class SelectCoverGalleryFragment :
|
|||
)
|
||||
}
|
||||
|
||||
val getContent = registerForActivityResult(GetImageContract()) { uri: Uri? ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
val path = uri.parseImagePath(requireContext())
|
||||
vm.onImagePicked(ctx, path)
|
||||
} catch (e: Exception) {
|
||||
toast("Error while parsing path for cover image")
|
||||
Timber.d(e, "Error while parsing path for cover image")
|
||||
}
|
||||
} else {
|
||||
toast("Error while upload cover image, URI is null")
|
||||
Timber.e("Error while upload cover image, URI is null")
|
||||
}
|
||||
private val getContent = try { getContentLauncher() } catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
private val permissionReadStorage =
|
||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
|
||||
private fun getContentLauncher() = registerForActivityResult(GetImageContract()) { uri: Uri? ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
val path = uri.parseImagePath(requireContext())
|
||||
vm.onImagePicked(ctx, path)
|
||||
} catch (e: Exception) {
|
||||
toast("Error while parsing path for cover image")
|
||||
Timber.d(e, "Error while parsing path for cover image")
|
||||
}
|
||||
} else {
|
||||
toast("Error while upload cover image, URI is null")
|
||||
Timber.e("Error while upload cover image, URI is null")
|
||||
}
|
||||
}
|
||||
|
||||
private val permissionReadStorage = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
|
||||
val readResult = grantResults[Manifest.permission.READ_EXTERNAL_STORAGE]
|
||||
if (readResult == true) {
|
||||
openGallery()
|
||||
|
@ -159,7 +162,12 @@ abstract class SelectCoverGalleryFragment :
|
|||
}
|
||||
|
||||
private fun openGallery() {
|
||||
getContent.launch(SELECT_IMAGE_CODE)
|
||||
try {
|
||||
getContent?.launch(SELECT_IMAGE_CODE)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while opening gallery")
|
||||
toast("Error while opening gallery: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasReadStoragePermission(): Boolean = ContextCompat.checkSelfPermission(
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary
|
|||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
|
||||
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.drawable
|
||||
import com.anytypeio.anytype.core_utils.ext.hideSoftInput
|
||||
|
@ -216,6 +217,11 @@ class BacklinkOrAddToObjectFragment :
|
|||
setDrawable(drawable(R.drawable.divider_object_search))
|
||||
}
|
||||
)
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
lastItemSpacingBottom = resources.getDimension(R.dimen.dp_120).toInt()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.anytypeio.anytype.R
|
|||
import com.anytypeio.anytype.analytics.base.EventsDictionary
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
|
||||
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.argOrNull
|
||||
import com.anytypeio.anytype.core_utils.ext.drawable
|
||||
|
@ -224,6 +225,11 @@ class LinkToObjectFragment : BaseBottomSheetTextInputFragment<FragmentObjectSear
|
|||
setDrawable(drawable(R.drawable.divider_object_search))
|
||||
}
|
||||
)
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
lastItemSpacingBottom = resources.getDimension(R.dimen.dp_120).toInt()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.ColorMnemonicPhrase
|
||||
import com.anytypeio.anytype.core_ui.ColorMnemonicStub
|
||||
import com.anytypeio.anytype.core_ui.ColorPagerIndicator
|
||||
import com.anytypeio.anytype.core_ui.ColorPagerIndicatorCurrent
|
||||
|
@ -45,6 +44,7 @@ import com.anytypeio.anytype.core_ui.ColorPagerIndicatorText
|
|||
import com.anytypeio.anytype.core_ui.ColorPlaceholderText
|
||||
import com.anytypeio.anytype.core_ui.ColorTextInput
|
||||
import com.anytypeio.anytype.core_ui.ColorTextInputCursor
|
||||
import com.anytypeio.anytype.core_ui.MnemonicPhrasePaletteColors
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineOnBoardingDescription
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle1Regular
|
||||
|
@ -110,20 +110,29 @@ fun PagerIndicator(
|
|||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun MnemonicPhraseWidget(
|
||||
modifier: Modifier = Modifier, mnemonic: String
|
||||
modifier: Modifier = Modifier,
|
||||
mnemonic: String,
|
||||
mnemonicColorPalette: List<Color>
|
||||
) {
|
||||
val words = mnemonic.split(" ")
|
||||
FlowRow(
|
||||
modifier = modifier, maxItemsInEachRow = 4, horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
words.forEach { word ->
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 3.dp),
|
||||
text = word.replaceFirst(word.first(), word.first().uppercaseChar()),
|
||||
style = PreviewTitle1Regular.copy(
|
||||
color = ColorMnemonicPhrase
|
||||
if (words.isNotEmpty()) {
|
||||
FlowRow(
|
||||
modifier = modifier, maxItemsInEachRow = 4, horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
words.forEachIndexed { idx, word ->
|
||||
val color = if (idx <= mnemonicColorPalette.lastIndex) {
|
||||
mnemonicColorPalette[idx]
|
||||
} else {
|
||||
MnemonicPhrasePaletteColors.random()
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 6.dp, vertical = 3.dp),
|
||||
text = word.lowercase(),
|
||||
style = PreviewTitle1Regular.copy(
|
||||
color = color
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +194,10 @@ fun MnemonicStubPreview() {
|
|||
@Composable
|
||||
fun MnemonicPhraseWidgetPreview() {
|
||||
val mnemonic = "kenobi hello there general grievous you are bold like toe pineapple wave"
|
||||
MnemonicPhraseWidget(mnemonic = mnemonic)
|
||||
MnemonicPhraseWidget(
|
||||
mnemonic = mnemonic,
|
||||
mnemonicColorPalette = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.anytypeio.anytype.ui.onboarding
|
|||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
|
@ -11,12 +10,12 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.compose.animation.AnimatedContentScope.SlideDirection.Companion.Left
|
||||
import androidx.compose.animation.AnimatedContentScope.SlideDirection.Companion.Right
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
|
@ -25,9 +24,12 @@ import androidx.compose.animation.fadeIn
|
|||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
@ -42,18 +44,26 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.navOptions
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.BuildConfig
|
||||
import com.anytypeio.anytype.core_ui.MNEMONIC_WORD_COUNT
|
||||
import com.anytypeio.anytype.core_ui.MnemonicPhrasePaletteColors
|
||||
import com.anytypeio.anytype.core_ui.views.BaseAlertDialog
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.insets.RootViewDeferringInsetsCallback
|
||||
import com.anytypeio.anytype.di.common.ComponentManager
|
||||
|
@ -61,6 +71,7 @@ import com.anytypeio.anytype.di.common.componentManager
|
|||
import com.anytypeio.anytype.ext.daggerViewModel
|
||||
import com.anytypeio.anytype.presentation.onboarding.OnboardingStartViewModel
|
||||
import com.anytypeio.anytype.presentation.onboarding.OnboardingStartViewModel.SideEffect
|
||||
import com.anytypeio.anytype.presentation.onboarding.OnboardingViewModel
|
||||
import com.anytypeio.anytype.presentation.onboarding.login.OnboardingLoginSetupViewModel
|
||||
import com.anytypeio.anytype.presentation.onboarding.login.OnboardingMnemonicLoginViewModel
|
||||
import com.anytypeio.anytype.presentation.onboarding.signup.OnboardingSoulCreationViewModel
|
||||
|
@ -85,11 +96,35 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
|
|||
import com.google.android.exoplayer2.upstream.RawResourceDataSource
|
||||
import com.google.android.exoplayer2.util.Util
|
||||
import com.google.zxing.integration.android.IntentIntegrator
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.delay
|
||||
import timber.log.Timber
|
||||
|
||||
class OnboardingFragment : Fragment() {
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Inject
|
||||
lateinit var factory: OnboardingViewModel.Factory
|
||||
|
||||
private val onBoardingViewModel by viewModels<OnboardingViewModel> { factory }
|
||||
|
||||
private val mnemonicColorPalette by lazy {
|
||||
buildList {
|
||||
repeat(MNEMONIC_WORD_COUNT) {
|
||||
add(MnemonicPhrasePaletteColors.random())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
injectDependencies()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
releaseDependencies()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
@ -98,24 +133,50 @@ class OnboardingFragment : Fragment() {
|
|||
return ComposeView(requireContext()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
val navController = rememberAnimatedNavController()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black)
|
||||
) {
|
||||
val currentPage = remember { mutableStateOf(OnboardingPage.AUTH) }
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
BackgroundCircle()
|
||||
}
|
||||
Onboarding(currentPage, navController)
|
||||
PagerIndicator(
|
||||
pageCount = OnboardingPage.values().filter { it.visible }.size,
|
||||
page = currentPage,
|
||||
onBackClick = { navController.popBackStack() }
|
||||
)
|
||||
OnboardingScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
private fun OnboardingScreen() {
|
||||
MaterialTheme {
|
||||
val navController = rememberAnimatedNavController()
|
||||
val defaultBackCallback: BackButtonCallback = { navController.popBackStack() }
|
||||
val signUpBackButtonCallback = remember {
|
||||
mutableStateOf(defaultBackCallback)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black)
|
||||
) {
|
||||
val currentPage = remember { mutableStateOf(OnboardingPage.AUTH) }
|
||||
BackgroundCircle()
|
||||
Onboarding(
|
||||
currentPage = currentPage,
|
||||
navController = navController,
|
||||
backButtonCallback = signUpBackButtonCallback
|
||||
)
|
||||
PagerIndicator(
|
||||
pageCount = OnboardingPage.values().filter { it.visible }.size,
|
||||
page = currentPage,
|
||||
onBackClick = {
|
||||
signUpBackButtonCallback.value?.invoke()
|
||||
}
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
onBoardingViewModel.toasts.collect {
|
||||
toast(it)
|
||||
}
|
||||
}
|
||||
DisposableEffect(
|
||||
viewLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.DESTROYED)
|
||||
) {
|
||||
onDispose {
|
||||
signUpBackButtonCallback.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +187,14 @@ class OnboardingFragment : Fragment() {
|
|||
onApplyWindowRootInsets(view)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
// Disabling workaround to prevent background circle with video shrinking.
|
||||
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onApplyWindowRootInsets(view: View) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val deferringInsetsListener = RootViewDeferringInsetsCallback(
|
||||
|
@ -135,12 +204,19 @@ class OnboardingFragment : Fragment() {
|
|||
|
||||
ViewCompat.setWindowInsetsAnimationCallback(view, deferringInsetsListener)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view, deferringInsetsListener)
|
||||
} else {
|
||||
// Enabling workaround to prevent background circle with video shrinking.
|
||||
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
private fun Onboarding(currentPage: MutableState<OnboardingPage>, navController: NavHostController) {
|
||||
private fun Onboarding(
|
||||
currentPage: MutableState<OnboardingPage>,
|
||||
backButtonCallback: MutableState<BackButtonCallback>,
|
||||
navController: NavHostController
|
||||
) {
|
||||
AnimatedNavHost(navController, startDestination = OnboardingNavigation.auth) {
|
||||
composable(
|
||||
route = OnboardingNavigation.auth,
|
||||
|
@ -188,25 +264,25 @@ class OnboardingFragment : Fragment() {
|
|||
}
|
||||
) {
|
||||
currentPage.value = OnboardingPage.VOID
|
||||
val component = componentManager().onboardingNewVoidComponent.ReleaseOn(
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
state = Lifecycle.State.DESTROYED
|
||||
)
|
||||
val component = componentManager().onboardingNewVoidComponent
|
||||
val vm = daggerViewModel { component.get().getViewModel() }
|
||||
|
||||
VoidScreenWrapper(
|
||||
contentPaddingTop = ContentPaddingTop(),
|
||||
onNextClicked = vm::onNextClicked,
|
||||
screenState = vm.state.collectAsState().value
|
||||
)
|
||||
BackHandler {
|
||||
vm.onSystemBackPressed()
|
||||
}
|
||||
|
||||
backButtonCallback.value = vm::onBackButtonPressed
|
||||
BackHandler { vm.onSystemBackPressed() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
vm.navigation.collect { navigation ->
|
||||
when(navigation) {
|
||||
when (navigation) {
|
||||
OnboardingVoidViewModel.Navigation.GoBack -> {
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
OnboardingVoidViewModel.Navigation.NavigateToMnemonic -> {
|
||||
navController.navigate(OnboardingNavigation.mnemonic)
|
||||
}
|
||||
|
@ -216,6 +292,9 @@ class OnboardingFragment : Fragment() {
|
|||
LaunchedEffect(Unit) {
|
||||
vm.toasts.collect { toast(it) }
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { component.release() }
|
||||
}
|
||||
}
|
||||
composable(
|
||||
route = OnboardingNavigation.mnemonic,
|
||||
|
@ -224,6 +303,7 @@ class OnboardingFragment : Fragment() {
|
|||
OnboardingNavigation.void -> {
|
||||
slideIntoContainer(Left, tween(ANIMATION_LENGTH_SLIDE))
|
||||
}
|
||||
|
||||
else -> {
|
||||
slideIntoContainer(Right, tween(ANIMATION_LENGTH_SLIDE))
|
||||
}
|
||||
|
@ -234,6 +314,7 @@ class OnboardingFragment : Fragment() {
|
|||
OnboardingNavigation.void -> {
|
||||
slideOutOfContainer(Right, tween(ANIMATION_LENGTH_SLIDE))
|
||||
}
|
||||
|
||||
else -> {
|
||||
slideOutOfContainer(Left, tween(ANIMATION_LENGTH_SLIDE))
|
||||
}
|
||||
|
@ -241,7 +322,12 @@ class OnboardingFragment : Fragment() {
|
|||
}
|
||||
) {
|
||||
currentPage.value = OnboardingPage.MNEMONIC
|
||||
Mnemonic(navController, ContentPaddingTop())
|
||||
backButtonCallback.value = { navController.popBackStack() }
|
||||
Mnemonic(
|
||||
navController = navController,
|
||||
contentPaddingTop = ContentPaddingTop(),
|
||||
mnemonicColorPalette = mnemonicColorPalette
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = OnboardingNavigation.createSoul,
|
||||
|
@ -251,6 +337,7 @@ class OnboardingFragment : Fragment() {
|
|||
OnboardingNavigation.mnemonic -> {
|
||||
slideOutOfContainer(Right, tween(ANIMATION_LENGTH_SLIDE))
|
||||
}
|
||||
|
||||
else -> {
|
||||
fadeOut(tween(ANIMATION_LENGTH_FADE))
|
||||
}
|
||||
|
@ -258,6 +345,7 @@ class OnboardingFragment : Fragment() {
|
|||
}
|
||||
) {
|
||||
currentPage.value = OnboardingPage.SOUL_CREATION
|
||||
backButtonCallback.value = { navController.popBackStack() }
|
||||
CreateSoul(
|
||||
navController = navController,
|
||||
contentPaddingTop = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||
|
@ -273,9 +361,10 @@ class OnboardingFragment : Fragment() {
|
|||
}
|
||||
) {
|
||||
currentPage.value = OnboardingPage.SOUL_CREATION_ANIM
|
||||
backButtonCallback.value = null
|
||||
CreateSoulAnimation(ContentPaddingTop())
|
||||
BackHandler {
|
||||
Timber.d("OnBackHandler")
|
||||
// Intercepting back-press event but doing nothing.
|
||||
}
|
||||
}
|
||||
composable(
|
||||
|
@ -296,13 +385,12 @@ class OnboardingFragment : Fragment() {
|
|||
@Composable
|
||||
private fun ContentPaddingTop() = LocalConfiguration.current.screenHeightDp * 2 / 6
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun Recovery(navController: NavHostController) {
|
||||
val component = componentManager().onboardingMnemonicLoginComponent.ReleaseOn(
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
state = Lifecycle.State.DESTROYED
|
||||
)
|
||||
val component = componentManager().onboardingMnemonicLoginComponent
|
||||
val vm = daggerViewModel { component.get().getViewModel() }
|
||||
val isQrWarningDialogVisible = remember { mutableStateOf(false) }
|
||||
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
|
@ -317,46 +405,57 @@ class OnboardingFragment : Fragment() {
|
|||
vm = vm,
|
||||
onBackClicked = vm::onBackButtonPressed,
|
||||
onScanQrClick = {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setMessage(R.string.alert_qr_camera)
|
||||
.setPositiveButton(R.string.alert_qr_camera_ok) { dialog, _ ->
|
||||
proceedWithQrCodeActivity(launcher, dialog)
|
||||
}
|
||||
.setCancelable(true)
|
||||
.show()
|
||||
}
|
||||
isQrWarningDialogVisible.value = true
|
||||
vm.onScanQrCodeClicked()
|
||||
},
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
vm.sideEffects.collect { effect ->
|
||||
when(effect) {
|
||||
when (effect) {
|
||||
is OnboardingMnemonicLoginViewModel.SideEffect.Exit -> {
|
||||
navController.popBackStack()
|
||||
val lastDestination = navController.backQueue.lastOrNull()
|
||||
// TODO Temporary workaround to prevent inconsistent state in navigation
|
||||
if (lastDestination?.destination?.route == OnboardingNavigation.recovery) {
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
is OnboardingMnemonicLoginViewModel.SideEffect.ProceedWithLogin -> {
|
||||
navController.navigate(OnboardingNavigation.enterTheVoid)
|
||||
}
|
||||
|
||||
is OnboardingMnemonicLoginViewModel.SideEffect.Error -> {
|
||||
toast(effect.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isQrWarningDialogVisible.value) {
|
||||
BaseAlertDialog(
|
||||
dialogText = stringResource(id = R.string.alert_qr_camera),
|
||||
buttonText = stringResource(id = R.string.alert_qr_camera_ok),
|
||||
onButtonClick = {
|
||||
isQrWarningDialogVisible.value = false
|
||||
proceedWithQrCodeActivity(launcher)
|
||||
},
|
||||
onDismissRequest = { isQrWarningDialogVisible.value = false }
|
||||
)
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { component.release() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithQrCodeActivity(
|
||||
launcher: ManagedActivityResultLauncher<Intent, ActivityResult>,
|
||||
dialog: DialogInterface
|
||||
) {
|
||||
private fun proceedWithQrCodeActivity(launcher: ManagedActivityResultLauncher<Intent, ActivityResult>) {
|
||||
try {
|
||||
launcher.launch(
|
||||
IntentIntegrator
|
||||
.forSupportFragment(this)
|
||||
.setBeepEnabled(false)
|
||||
.createScanIntent()
|
||||
).also {
|
||||
dialog.dismiss()
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
toast("Error while scanning QR code")
|
||||
Timber.e(e, "Error while scanning QR code")
|
||||
}
|
||||
}
|
||||
|
@ -365,10 +464,7 @@ class OnboardingFragment : Fragment() {
|
|||
private fun enterTheVoid(
|
||||
navController: NavHostController
|
||||
) {
|
||||
val component = componentManager().onboardingLoginSetupComponent.ReleaseOn(
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
state = Lifecycle.State.DESTROYED
|
||||
)
|
||||
val component = componentManager().onboardingLoginSetupComponent
|
||||
val vm = daggerViewModel { component.get().getViewModel() }
|
||||
EnteringTheVoidScreen(
|
||||
error = vm.error.collectAsState().value,
|
||||
|
@ -381,52 +477,70 @@ class OnboardingFragment : Fragment() {
|
|||
OnboardingLoginSetupViewModel.Navigation.Exit -> {
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
OnboardingLoginSetupViewModel.Navigation.NavigateToHomeScreen -> {
|
||||
findNavController().navigate(R.id.action_openHome)
|
||||
}
|
||||
|
||||
OnboardingLoginSetupViewModel.Navigation.NavigateToMigrationErrorScreen -> {
|
||||
// TODO
|
||||
findNavController().navigate(R.id.migrationNeededScreen, null, navOptions {
|
||||
popUpTo(R.id.onboarding_nav) {
|
||||
inclusive = false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
vm.toasts.collect { toast(it) }
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { component.release() }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateSoulAnimation(contentPaddingTop: Int) {
|
||||
val component = componentManager().onboardingSoulCreationAnimComponent.ReleaseOn(
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
state = Lifecycle.State.DESTROYED
|
||||
)
|
||||
val component = componentManager().onboardingSoulCreationAnimComponent
|
||||
CreateSoulAnimWrapper(
|
||||
contentPaddingTop = contentPaddingTop,
|
||||
viewModel = daggerViewModel { component.get().getViewModel() }
|
||||
) {
|
||||
findNavController().navigate(R.id.action_openHome)
|
||||
viewModel = daggerViewModel { component.get().getViewModel() },
|
||||
onAnimationComplete = {
|
||||
findNavController().navigate(R.id.action_openHome)
|
||||
}
|
||||
)
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { component.release() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun CreateSoul(navController: NavHostController, contentPaddingTop: Int) {
|
||||
val component = componentManager().onboardingSoulCreationComponent.ReleaseOn(
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
state = Lifecycle.State.DESTROYED
|
||||
)
|
||||
val component = componentManager().onboardingSoulCreationComponent
|
||||
val vm = daggerViewModel { component.get().getViewModel() }
|
||||
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboardInsets = WindowInsets.ime
|
||||
val density = LocalDensity.current
|
||||
|
||||
CreateSoulWrapper(vm, contentPaddingTop)
|
||||
|
||||
val navigationCommands = vm.navigationFlow.collectAsState(
|
||||
initial = OnboardingSoulCreationViewModel.Navigation.Idle
|
||||
)
|
||||
LaunchedEffect(key1 = navigationCommands.value) {
|
||||
when (navigationCommands.value) {
|
||||
is OnboardingSoulCreationViewModel.Navigation.OpenSoulCreationAnim -> {
|
||||
navController.navigate(
|
||||
route = OnboardingNavigation.createSoulAnim
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
vm.navigationFlow.collect { command ->
|
||||
when (command) {
|
||||
is OnboardingSoulCreationViewModel.Navigation.OpenSoulCreationAnim -> {
|
||||
if (keyboardInsets.getBottom(density) > 0) {
|
||||
focusManager.clearFocus(force = true)
|
||||
delay(KEYBOARD_HIDE_DELAY)
|
||||
}
|
||||
navController.navigate(
|
||||
route = OnboardingNavigation.createSoulAnim
|
||||
)
|
||||
vm.sendAnalyticsOnboardingScreen()
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
|
@ -434,22 +548,33 @@ class OnboardingFragment : Fragment() {
|
|||
toast(it)
|
||||
}
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { component.release() }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Mnemonic(navController: NavHostController, contentPaddingTop: Int) {
|
||||
val component = componentManager().onboardingMnemonicComponent.ReleaseOn(
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
state = Lifecycle.State.DESTROYED
|
||||
)
|
||||
private fun Mnemonic(
|
||||
navController: NavHostController,
|
||||
contentPaddingTop: Int,
|
||||
mnemonicColorPalette: List<Color>
|
||||
) {
|
||||
val component = componentManager().onboardingMnemonicComponent
|
||||
val vm = daggerViewModel { component.get().getViewModel() }
|
||||
MnemonicPhraseScreenWrapper(
|
||||
contentPaddingTop = contentPaddingTop,
|
||||
viewModel = daggerViewModel { component.get().getViewModel() },
|
||||
viewModel = vm,
|
||||
openSoulCreation = {
|
||||
navController.navigate(OnboardingNavigation.createSoul)
|
||||
vm.sendAnalyticsOnboardingScreen()
|
||||
},
|
||||
copyMnemonicToClipboard = ::copyMnemonicToClipboard
|
||||
copyMnemonicToClipboard = ::copyMnemonicToClipboard,
|
||||
vm = vm,
|
||||
mnemonicColorPalette = mnemonicColorPalette
|
||||
)
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { component.release() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyMnemonicToClipboard(mnemonicPhrase: String) {
|
||||
|
@ -467,10 +592,7 @@ class OnboardingFragment : Fragment() {
|
|||
|
||||
@Composable
|
||||
private fun Auth(navController: NavHostController) {
|
||||
val component = componentManager().onboardingStartComponent.ReleaseOn(
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
state = Lifecycle.State.DESTROYED
|
||||
)
|
||||
val component = componentManager().onboardingStartComponent
|
||||
val vm = daggerViewModel { component.get().getViewModel() }
|
||||
AuthScreenWrapper(vm = vm)
|
||||
LaunchedEffect(Unit) {
|
||||
|
@ -507,6 +629,7 @@ class OnboardingFragment : Fragment() {
|
|||
when (navigation) {
|
||||
is OnboardingStartViewModel.AuthNavigation.ProceedWithSignUp -> {
|
||||
navController.navigate(OnboardingNavigation.void)
|
||||
vm.sendAnalyticsOnboardingScreen()
|
||||
}
|
||||
|
||||
is OnboardingStartViewModel.AuthNavigation.ProceedWithSignIn -> {
|
||||
|
@ -515,6 +638,9 @@ class OnboardingFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { component.release() }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -569,25 +695,17 @@ class OnboardingFragment : Fragment() {
|
|||
return player
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> ComponentManager.Component<T>.ReleaseOn(
|
||||
viewLifecycleOwner: LifecycleOwner,
|
||||
state: Lifecycle.State
|
||||
): ComponentManager.Component<T> {
|
||||
val that = this
|
||||
DisposableEffect(
|
||||
key1 = viewLifecycleOwner.lifecycle.currentState.isAtLeast(state),
|
||||
effect = {
|
||||
onDispose {
|
||||
if (viewLifecycleOwner.lifecycle.currentState == state) {
|
||||
that.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
return that
|
||||
fun injectDependencies() {
|
||||
componentManager().onboardingComponent.get().inject(this)
|
||||
}
|
||||
|
||||
fun releaseDependencies() {
|
||||
componentManager().onboardingComponent.release()
|
||||
}
|
||||
}
|
||||
|
||||
private const val ANIMATION_LENGTH_SLIDE = 300
|
||||
private const val ANIMATION_LENGTH_FADE = 700
|
||||
private const val ANIMATION_LENGTH_FADE = 700
|
||||
private const val KEYBOARD_HIDE_DELAY = 300L
|
||||
|
||||
typealias BackButtonCallback = (() -> Unit)?
|
|
@ -4,8 +4,10 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
|
@ -22,6 +24,7 @@ import androidx.compose.ui.text.withStyle
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonInversion
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextSecondaryColor
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
|
@ -68,6 +71,7 @@ fun AuthScreen(
|
|||
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center) {
|
||||
Title(modifier = Modifier)
|
||||
Description()
|
||||
Spacer(modifier = Modifier.height(72.dp))
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
@ -142,7 +146,8 @@ fun SignButtons(
|
|||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.padding(start = 6.dp, end = 32.dp)
|
||||
.padding(start = 6.dp, end = 32.dp),
|
||||
textColor = ColorButtonInversion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.ui.onboarding.screens.signin
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
@ -8,17 +9,35 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonRegular
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
|
||||
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun EnteringTheVoidScreenPreview() {
|
||||
EnteringTheVoidScreen(
|
||||
error = "",
|
||||
contentPaddingTop = 10,
|
||||
onSystemBackPressed = {}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EnteringTheVoidScreen(
|
||||
|
@ -26,6 +45,8 @@ fun EnteringTheVoidScreen(
|
|||
contentPaddingTop: Int,
|
||||
onSystemBackPressed: () -> Unit
|
||||
) {
|
||||
val loadingAlpha by animateFloatAsState(targetValue = 1f)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
@ -51,6 +72,18 @@ fun EnteringTheVoidScreen(
|
|||
fontSize = 10.sp
|
||||
)
|
||||
)
|
||||
} else {
|
||||
DotsLoadingIndicator(
|
||||
animating = true,
|
||||
modifier = Modifier
|
||||
.padding(top = 20.dp)
|
||||
.graphicsLayer { alpha = loadingAlpha }
|
||||
.align(Alignment.CenterHorizontally)
|
||||
,
|
||||
animationSpecs = FadeAnimationSpecs(itemCount = 3),
|
||||
size = ButtonSize.XSmall,
|
||||
color = ColorButtonRegular
|
||||
)
|
||||
}
|
||||
}
|
||||
BackHandler {
|
||||
|
|
|
@ -23,6 +23,7 @@ import androidx.compose.ui.text.input.ImeAction
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonRegular
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
|
@ -147,7 +148,8 @@ fun RecoveryScreen(
|
|||
size = ButtonSize.Large,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 18.dp, end = 18.dp, bottom = 24.dp)
|
||||
.padding(start = 18.dp, end = 18.dp, bottom = 24.dp),
|
||||
textColor = ColorButtonRegular
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
@ -32,6 +33,8 @@ import androidx.compose.ui.geometry.Offset
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
@ -63,44 +66,41 @@ private fun CreateSoulAnimScreen(
|
|||
onAnimationComplete: () -> Unit,
|
||||
contentPaddingTop: Int
|
||||
) {
|
||||
|
||||
state.fold(
|
||||
onSuccess = {
|
||||
|
||||
val gradient = Brush.radialGradient(
|
||||
listOf(
|
||||
Color(it.icon.from.toColorInt()),
|
||||
Color(it.icon.to.toColorInt())
|
||||
)
|
||||
)
|
||||
|
||||
val isBoxVisible = remember { mutableStateOf(false) }
|
||||
val targetElementsAlpha by animateFloatAsState(
|
||||
targetValue = if (isBoxVisible.value) 1f else 0f,
|
||||
animationSpec = tween(durationMillis = ANIMATION_DURATION)
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Top) {
|
||||
Spacer(modifier = Modifier.height(contentPaddingTop.dp))
|
||||
CreateSoulAnimationTitle(targetElementsAlpha)
|
||||
CreateSoulAnimation(
|
||||
isBoxVisible,
|
||||
targetElementsAlpha,
|
||||
gradient,
|
||||
it.name
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(ANIMATION_START_DELAY)
|
||||
isBoxVisible.value = true
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
delay(ANIMATION_COMPLETE_DELAY)
|
||||
onAnimationComplete.invoke()
|
||||
}
|
||||
}
|
||||
val isBoxVisible = remember { mutableStateOf(false) }
|
||||
val targetElementsAlpha by animateFloatAsState(
|
||||
targetValue = if (isBoxVisible.value) 1f else 0f,
|
||||
animationSpec = tween(durationMillis = ANIMATION_DURATION)
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
state.fold(
|
||||
onSuccess = {
|
||||
val gradient = Brush.radialGradient(
|
||||
listOf(
|
||||
Color(it.icon.from.toColorInt()),
|
||||
Color(it.icon.to.toColorInt())
|
||||
)
|
||||
)
|
||||
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Top) {
|
||||
Spacer(modifier = Modifier.height(contentPaddingTop.dp))
|
||||
CreateSoulAnimationTitle(targetElementsAlpha)
|
||||
CreateSoulAnimation(
|
||||
isBoxVisible,
|
||||
targetElementsAlpha,
|
||||
gradient,
|
||||
it.name
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
delay(ANIMATION_START_DELAY)
|
||||
isBoxVisible.value = true
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
delay(ANIMATION_COMPLETE_DELAY)
|
||||
onAnimationComplete.invoke()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -186,6 +186,9 @@ private fun CreateSoulAnimation(
|
|||
.offset(x = -circleOffset.value)
|
||||
.padding(top = 88.dp)
|
||||
.align(Alignment.Center)
|
||||
.widthIn(max = 128.dp),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Name
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextSecondaryColor
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
|
@ -43,15 +44,16 @@ import com.anytypeio.anytype.ui.onboarding.OnboardingInput
|
|||
|
||||
@Composable
|
||||
fun CreateSoulWrapper(viewModel: OnboardingSoulCreationViewModel, contentPaddingTop: Int) {
|
||||
CreateSoulScreen(contentPaddingTop) {
|
||||
viewModel.setAccountAndSpaceName(it)
|
||||
}
|
||||
CreateSoulScreen(
|
||||
contentPaddingTop = contentPaddingTop,
|
||||
onGoToTheAppClicked = viewModel::onGoToTheAppClicked
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateSoulScreen(
|
||||
contentPaddingTop: Int,
|
||||
onCreateSoulClicked: (String) -> Unit
|
||||
onGoToTheAppClicked: (Name) -> Unit
|
||||
) {
|
||||
val text = remember { mutableStateOf("") }
|
||||
val isKeyboardVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
|
||||
|
@ -80,11 +82,11 @@ private fun CreateSoulScreen(
|
|||
item {
|
||||
CreateSoulInput(
|
||||
text = text,
|
||||
onKeyboardActionDoneClicked = { onCreateSoulClicked(text.value) }
|
||||
onKeyboardActionDoneClicked = { onGoToTheAppClicked(text.value) }
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(9.dp))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
item {
|
||||
CreateSoulDescription()
|
||||
|
@ -103,7 +105,7 @@ private fun CreateSoulScreen(
|
|||
Modifier.padding(bottom = 13.dp)
|
||||
)
|
||||
,
|
||||
onCreateSoulEntered = onCreateSoulClicked,
|
||||
onGoToTheAppClicked = onGoToTheAppClicked,
|
||||
text = text
|
||||
)
|
||||
}
|
||||
|
@ -152,7 +154,7 @@ fun CreateSoulInput(
|
|||
.focusRequester(focusRequester)
|
||||
,
|
||||
text = text,
|
||||
placeholder = stringResource(id = R.string.onboarding_soul_creation_placeholder),
|
||||
placeholder = stringResource(id = R.string.name),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
val input = text.value
|
||||
|
@ -181,6 +183,7 @@ fun CreateSoulDescription() {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 48.dp)
|
||||
.wrapContentHeight(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
|
@ -197,13 +200,13 @@ fun CreateSoulDescription() {
|
|||
@Composable
|
||||
fun CreateSoulNextButton(
|
||||
modifier: Modifier,
|
||||
onCreateSoulEntered: (String) -> Unit,
|
||||
onGoToTheAppClicked: (Name) -> Unit,
|
||||
text: MutableState<String>
|
||||
) {
|
||||
OnBoardingButtonPrimary(
|
||||
text = stringResource(id = R.string.go_to_the_app),
|
||||
onClick = {
|
||||
onCreateSoulEntered.invoke(text.value)
|
||||
onGoToTheAppClicked(text.value)
|
||||
},
|
||||
size = ButtonSize.Large,
|
||||
enabled = text.value.trim().isNotEmpty(),
|
||||
|
|
|
@ -19,12 +19,14 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.ColorBackgroundField
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonRegular
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextSecondaryColor
|
||||
import com.anytypeio.anytype.core_ui.extensions.conditional
|
||||
|
@ -42,7 +44,9 @@ fun MnemonicPhraseScreenWrapper(
|
|||
viewModel: OnboardingMnemonicViewModel,
|
||||
openSoulCreation: () -> Unit,
|
||||
copyMnemonicToClipboard: (String) -> Unit,
|
||||
contentPaddingTop: Int
|
||||
contentPaddingTop: Int,
|
||||
vm: OnboardingMnemonicViewModel,
|
||||
mnemonicColorPalette: List<Color>
|
||||
) {
|
||||
val state = viewModel.state.collectAsStateWithLifecycle().value
|
||||
MnemonicPhraseScreen(
|
||||
|
@ -50,7 +54,9 @@ fun MnemonicPhraseScreenWrapper(
|
|||
reviewMnemonic = { viewModel.openMnemonic() },
|
||||
openSoulCreation = openSoulCreation,
|
||||
copyMnemonicToClipboard = copyMnemonicToClipboard,
|
||||
contentPaddingTop = contentPaddingTop
|
||||
contentPaddingTop = contentPaddingTop,
|
||||
vm = vm,
|
||||
mnemonicColorPalette = mnemonicColorPalette
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -60,7 +66,9 @@ fun MnemonicPhraseScreen(
|
|||
reviewMnemonic: () -> Unit,
|
||||
openSoulCreation: () -> Unit,
|
||||
copyMnemonicToClipboard: (String) -> Unit,
|
||||
contentPaddingTop: Int
|
||||
contentPaddingTop: Int,
|
||||
vm: OnboardingMnemonicViewModel,
|
||||
mnemonicColorPalette: List<Color>
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
|
@ -71,14 +79,19 @@ fun MnemonicPhraseScreen(
|
|||
) {
|
||||
Spacer(modifier = Modifier.height(contentPaddingTop.dp))
|
||||
MnemonicTitle()
|
||||
MnemonicPhrase(state, copyMnemonicToClipboard)
|
||||
MnemonicPhrase(
|
||||
state = state,
|
||||
copyMnemonicToClipboard = copyMnemonicToClipboard,
|
||||
mnemonicColorPalette = mnemonicColorPalette
|
||||
)
|
||||
MnemonicDescription()
|
||||
}
|
||||
MnemonicButtons(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
openMnemonic = reviewMnemonic,
|
||||
openSoulCreation = openSoulCreation,
|
||||
state = state
|
||||
state = state,
|
||||
vm = vm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +101,8 @@ fun MnemonicButtons(
|
|||
modifier: Modifier = Modifier,
|
||||
openMnemonic: () -> Unit,
|
||||
openSoulCreation: () -> Unit,
|
||||
state: OnboardingMnemonicViewModel.State
|
||||
state: OnboardingMnemonicViewModel.State,
|
||||
vm: OnboardingMnemonicViewModel
|
||||
) {
|
||||
Column(modifier.wrapContentHeight()) {
|
||||
when (state) {
|
||||
|
@ -125,7 +139,10 @@ fun MnemonicButtons(
|
|||
),
|
||||
onClick = {
|
||||
openSoulCreation.invoke()
|
||||
}, size = ButtonSize.Large
|
||||
vm.onCheckLaterClicked()
|
||||
},
|
||||
size = ButtonSize.Large,
|
||||
textColor = ColorButtonRegular
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +171,8 @@ fun MnemonicTitle() {
|
|||
@Composable
|
||||
fun MnemonicPhrase(
|
||||
state: OnboardingMnemonicViewModel.State,
|
||||
copyMnemonicToClipboard: (String) -> Unit
|
||||
copyMnemonicToClipboard: (String) -> Unit,
|
||||
mnemonicColorPalette: List<Color>
|
||||
) {
|
||||
when (state) {
|
||||
is OnboardingMnemonicViewModel.State.Idle -> {}
|
||||
|
@ -187,7 +205,8 @@ fun MnemonicPhrase(
|
|||
end = 16.dp,
|
||||
bottom = 16.dp
|
||||
),
|
||||
mnemonic = state.mnemonicPhrase
|
||||
mnemonic = state.mnemonicPhrase,
|
||||
mnemonicColorPalette = mnemonicColorPalette
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -199,7 +218,9 @@ fun MnemonicPhrase(
|
|||
.padding(bottom = 12.dp),
|
||||
onClick = {
|
||||
copyMnemonicToClipboard.invoke(state.mnemonicPhrase)
|
||||
}, size = ButtonSize.SmallSecondary
|
||||
},
|
||||
size = ButtonSize.SmallSecondary,
|
||||
textColor = ColorButtonRegular
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
package com.anytypeio.anytype.ui.onboarding.screens.signup
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -7,30 +8,32 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonRegular
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
|
||||
import com.anytypeio.anytype.core_ui.OnBoardingTextSecondaryColor
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineOnBoardingDescription
|
||||
import com.anytypeio.anytype.core_ui.views.OnBoardingButtonPrimary
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.presentation.common.ScreenState
|
||||
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
|
||||
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
|
||||
import com.anytypeio.anytype.presentation.onboarding.signup.OnboardingVoidViewModel
|
||||
|
||||
|
||||
@Composable
|
||||
fun VoidScreenWrapper(
|
||||
screenState: ScreenState,
|
||||
screenState: OnboardingVoidViewModel.ScreenState,
|
||||
contentPaddingTop: Int,
|
||||
onNextClicked: () -> Unit
|
||||
) {
|
||||
|
@ -43,7 +46,7 @@ fun VoidScreenWrapper(
|
|||
|
||||
@Composable
|
||||
fun VoidScreen(
|
||||
screenState: ScreenState,
|
||||
screenState: OnboardingVoidViewModel.ScreenState,
|
||||
onNextClicked: () -> Unit,
|
||||
contentPaddingTop: Int
|
||||
) {
|
||||
|
@ -64,20 +67,24 @@ fun VoidScreen(
|
|||
.padding(start = 16.dp, end = 16.dp, bottom = 24.dp)
|
||||
.align(Alignment.BottomCenter)
|
||||
) {
|
||||
val isLoading = screenState is OnboardingVoidViewModel.ScreenState.Loading
|
||||
OnBoardingButtonPrimary(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.next),
|
||||
onClick = { onNextClicked() },
|
||||
text = if (!isLoading) stringResource(id = R.string.next) else "",
|
||||
onClick = { if (!isLoading) onNextClicked() },
|
||||
size = ButtonSize.Large
|
||||
)
|
||||
if (screenState is ScreenState.Loading) {
|
||||
CircularProgressIndicator(
|
||||
if (isLoading) {
|
||||
val loadingAlpha by animateFloatAsState(targetValue = 1f)
|
||||
DotsLoadingIndicator(
|
||||
animating = true,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.padding(end = 20.dp)
|
||||
.size(18.dp),
|
||||
color = colorResource(R.color.shape_secondary),
|
||||
strokeWidth = 1.5.dp
|
||||
.graphicsLayer { alpha = loadingAlpha }
|
||||
.align(Alignment.Center)
|
||||
,
|
||||
animationSpecs = FadeAnimationSpecs(itemCount = 3),
|
||||
size = ButtonSize.XSmall,
|
||||
color = ColorButtonRegular
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.anytypeio.anytype.BuildConfig
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_ui.extensions.drawable
|
||||
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
|
||||
import com.anytypeio.anytype.core_ui.features.search.ObjectSearchDividerItemDecoration
|
||||
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
|
||||
import com.anytypeio.anytype.core_utils.ext.argOrNull
|
||||
import com.anytypeio.anytype.core_utils.ext.imm
|
||||
import com.anytypeio.anytype.core_utils.ext.invisible
|
||||
import com.anytypeio.anytype.core_utils.ext.syncFocusWithImeVisibility
|
||||
|
@ -44,6 +46,8 @@ class ObjectSearchFragment :
|
|||
@Inject
|
||||
lateinit var factory: ObjectSearchViewModelFactory
|
||||
|
||||
private val ctx: Id? get() = argOrNull(CTX_KEY)
|
||||
|
||||
private lateinit var clearSearchText: View
|
||||
private lateinit var filterInputField: EditText
|
||||
|
||||
|
@ -51,7 +55,9 @@ class ObjectSearchFragment :
|
|||
|
||||
private val searchAdapter by lazy {
|
||||
DefaultObjectViewAdapter(
|
||||
onDefaultObjectClicked = vm::onObjectClicked
|
||||
onDefaultObjectClicked = { obj ->
|
||||
vm.onObjectClicked(view = obj, ctx = ctx)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -191,7 +197,7 @@ class ObjectSearchFragment :
|
|||
)
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
lastItemSpacingBottom = resources.getDimension(com.anytypeio.anytype.core_ui.R.dimen.dp_48).toInt()
|
||||
lastItemSpacingBottom = resources.getDimension(R.dimen.dp_120).toInt()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -215,5 +221,6 @@ class ObjectSearchFragment :
|
|||
|
||||
companion object {
|
||||
const val EMPTY_FILTER_TEXT = ""
|
||||
const val CTX_KEY = "arg.search.ctx"
|
||||
}
|
||||
}
|
|
@ -1063,7 +1063,11 @@ open class ObjectSetFragment :
|
|||
if (childFragmentManager.backStackEntryCount > 0) {
|
||||
childFragmentManager.popBackStack()
|
||||
} else {
|
||||
vm.onSystemBackPressed()
|
||||
if (vm.isCustomizeViewPanelVisible.value) {
|
||||
vm.onHideViewerCustomizeSwiped()
|
||||
} else {
|
||||
vm.onSystemBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ class AboutAppFragment : BaseBottomSheetComposeFragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
expand()
|
||||
skipCollapsed()
|
||||
subscribe(vm.navigation) {
|
||||
when (it) {
|
||||
is AboutAppViewModel.Navigation.OpenExternalLink -> {
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.core.os.bundleOf
|
||||
|
@ -16,6 +17,7 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
|
||||
import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior
|
||||
import com.anytypeio.anytype.core_utils.ext.shareFile
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
|
@ -23,12 +25,15 @@ import com.anytypeio.anytype.di.common.componentManager
|
|||
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel
|
||||
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel.Command
|
||||
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel.Event
|
||||
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
|
||||
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase.Companion.ARG_CONTEXT_ID_KEY
|
||||
import com.anytypeio.anytype.ui.sets.ARG_SHOW_REMOVE_BUTTON
|
||||
import com.anytypeio.anytype.ui.settings.system.SettingsActivity
|
||||
import com.anytypeio.anytype.ui_settings.main.MainSettingScreen
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class MainSettingFragment : BaseBottomSheetComposeFragment() {
|
||||
|
||||
|
@ -38,6 +43,9 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
|
|||
@Inject
|
||||
lateinit var featureToggles: FeatureToggles
|
||||
|
||||
@Inject
|
||||
lateinit var uriFileProvider: UriFileProvider
|
||||
|
||||
private val vm by viewModels<MainSettingsViewModel> { factory }
|
||||
|
||||
private val onProfileClicked = {
|
||||
|
@ -131,16 +139,32 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
|
|||
safeNavigate(
|
||||
R.id.actionOpenImagePickerScreen, bundleOf(
|
||||
ARG_CONTEXT_ID_KEY to command.id,
|
||||
ARG_SHOW_REMOVE_BUTTON to true
|
||||
ARG_SHOW_REMOVE_BUTTON to command.showRemoveButton
|
||||
)
|
||||
)
|
||||
}
|
||||
Command.OpenFilesStorageScreen -> {
|
||||
is Command.OpenFilesStorageScreen -> {
|
||||
safeNavigate(R.id.actionOpenFilesStorageScreen)
|
||||
}
|
||||
|
||||
is Command.Toast -> {
|
||||
toast(msg = command.msg)
|
||||
toast(
|
||||
msg = command.msg,
|
||||
duration = if (command.isLongDuration)
|
||||
Toast.LENGTH_LONG
|
||||
else
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
is Command.ShareSpaceDebug -> {
|
||||
try {
|
||||
shareFile(
|
||||
uriFileProvider.getUriForFile(File(command.path))
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while sharing space debug").also {
|
||||
toast("Error while sharing space debug. Please try again later.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,10 +76,7 @@ class ProfileFragment : BaseBottomSheetComposeFragment() {
|
|||
onKeychainPhraseClicked = onKeychainPhraseClicked,
|
||||
onDeleteAccountClicked = { throttle { proceedWithAccountDeletion() } },
|
||||
onLogoutClicked = onLogoutClicked,
|
||||
onSpaceDebugClicked = { throttle { vm.onSpaceDebugClicked() } },
|
||||
isLogoutInProgress = vm.isLoggingOut.collectAsState().value,
|
||||
isDebugSpaceReportInProgress = vm.isDebugSpaceReportInProgress.collectAsState().value,
|
||||
isShowDebug = true,
|
||||
onNameChange = { vm.onNameChange(it) },
|
||||
onProfileIconClick = { proceedWithIconClick() },
|
||||
account = vm.profileData.collectAsStateWithLifecycle().value,
|
||||
|
|
|
@ -54,7 +54,7 @@ class MigrationErrorFragment : BaseComposeFragment() {
|
|||
browseUrl(command)
|
||||
}
|
||||
is MigrationErrorViewModel.Command.Exit -> {
|
||||
navigation().startLogin()
|
||||
navigation().exitFromMigrationScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
|
||||
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.argOrNull
|
||||
import com.anytypeio.anytype.core_utils.ext.drawable
|
||||
|
@ -205,6 +206,11 @@ class SelectWidgetSourceFragment : BaseBottomSheetTextInputFragment<FragmentObje
|
|||
setDrawable(drawable(R.drawable.divider_object_search))
|
||||
}
|
||||
)
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
lastItemSpacingBottom = resources.getDimension(R.dimen.dp_120).toInt()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -461,7 +461,7 @@ fun CollectionItem(
|
|||
.padding(0.dp, 0.dp, 60.dp, 0.dp)
|
||||
) {
|
||||
|
||||
val name = view.obj.name.ifBlank { stringResource(R.string.untitled) }
|
||||
val name = view.obj.name.trim().ifBlank { stringResource(R.string.untitled) }
|
||||
|
||||
Text(
|
||||
text = name,
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
|||
|
||||
@Composable
|
||||
fun TreeWidgetObjectIcon(
|
||||
modifier: Modifier = Modifier,
|
||||
icon: ObjectIcon,
|
||||
paddingStart: Dp,
|
||||
paddingEnd: Dp
|
||||
|
@ -38,7 +39,7 @@ fun TreeWidgetObjectIcon(
|
|||
when (icon) {
|
||||
is ObjectIcon.Profile.Avatar -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.height(18.dp)
|
||||
.width(18.dp)
|
||||
.padding(start = paddingStart, end = paddingEnd)
|
||||
|
@ -61,13 +62,13 @@ fun TreeWidgetObjectIcon(
|
|||
is ObjectIcon.Profile.Image -> {
|
||||
UriImage(
|
||||
uri = icon.hash,
|
||||
modifier = Modifier.padding(start = paddingStart, end = paddingEnd)
|
||||
modifier = modifier.padding(start = paddingStart, end = paddingEnd)
|
||||
)
|
||||
}
|
||||
is ObjectIcon.Basic.Emoji -> {
|
||||
UriImage(
|
||||
uri = Emojifier.safeUri(icon.unicode),
|
||||
modifier = Modifier.padding(start = paddingStart, end = paddingEnd)
|
||||
modifier = modifier.padding(start = paddingStart, end = paddingEnd)
|
||||
)
|
||||
}
|
||||
is ObjectIcon.Basic.Image -> {
|
||||
|
@ -89,7 +90,7 @@ fun TreeWidgetObjectIcon(
|
|||
else
|
||||
painterResource(id = R.drawable.ic_dashboard_task_checkbox_not_checked),
|
||||
contentDescription = "Task icon",
|
||||
modifier = Modifier.padding(start = paddingStart, end = paddingEnd)
|
||||
modifier = modifier.padding(start = paddingStart, end = paddingEnd)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
|
|
|
@ -14,6 +14,7 @@ import androidx.compose.material.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.res.colorResource
|
||||
|
@ -135,7 +136,7 @@ fun CompactListWidgetList(
|
|||
onWidgetElementClicked: (ObjectWrapper.Basic) -> Unit
|
||||
) {
|
||||
elements.forEachIndexed { idx, element ->
|
||||
Column() {
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 10.dp, horizontal = 16.dp)
|
||||
|
@ -149,11 +150,12 @@ fun CompactListWidgetList(
|
|||
TreeWidgetObjectIcon(
|
||||
icon = element.objectIcon,
|
||||
paddingStart = 8.dp,
|
||||
paddingEnd = 4.dp
|
||||
paddingEnd = 4.dp,
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
Text(
|
||||
text = element.obj.getWidgetObjectName() ?: stringResource(id = R.string.untitled),
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
modifier = Modifier.padding(start = 8.dp).fillMaxWidth(),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = PreviewTitle2Medium,
|
||||
|
|
|
@ -200,12 +200,13 @@ private fun TreeWidgetTreeItems(
|
|||
TreeWidgetObjectIcon(
|
||||
icon = element.objectIcon,
|
||||
paddingStart = 8.dp,
|
||||
paddingEnd = 4.dp
|
||||
paddingEnd = 4.dp,
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = element.obj.getWidgetObjectName() ?: stringResource(id = R.string.untitled),
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
modifier = Modifier.padding(start = 8.dp).fillMaxWidth(),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = PreviewTitle2Medium,
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0"/>
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1"/>
|
||||
</vector>
|
|
@ -4,167 +4,11 @@
|
|||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<group android:scaleX="1.01"
|
||||
android:scaleY="1.01"
|
||||
android:translateX="-0.54"
|
||||
android:translateY="-0.54">
|
||||
<path android:fillColor="#000000"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
|
@ -2,33 +2,34 @@
|
|||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
android:viewportWidth="110"
|
||||
android:viewportHeight="110">
|
||||
<group android:scaleX="1.01"
|
||||
android:scaleY="1.01"
|
||||
android:translateX="-0.55"
|
||||
android:translateY="-0.55">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h110v110h-110z"/>
|
||||
<path
|
||||
android:pathData="M0,0h110v110h-110z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M62.5,45.43V38H41.5V45.5L62.5,45.43ZM62.5,45.5H70.5V72H62.5V45.5ZM48.5,72.5C54.58,72.5 59.5,67.58 59.5,61.5C59.5,55.42 54.58,50.5 48.5,50.5C42.42,50.5 37.5,55.42 37.5,61.5C37.5,67.58 42.42,72.5 48.5,72.5Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
<gradient
|
||||
android:startX="37.5"
|
||||
android:startY="38"
|
||||
android:endX="53.81"
|
||||
android:endY="78.7"
|
||||
android:type="linear">
|
||||
<item android:offset="0.5" android:color="#FFFFFFFF"/>
|
||||
<item android:offset="0.66" android:color="#FFFFEDBE"/>
|
||||
<item android:offset="0.84" android:color="#FFFFBCC3"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</path>
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="140dp"
|
||||
android:height="30dp"
|
||||
android:viewportWidth="140"
|
||||
android:viewportHeight="30">
|
||||
android:width="121dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="121"
|
||||
android:viewportHeight="28">
|
||||
<path
|
||||
android:pathData="M19.369,4.757H14.168V7.71C13.128,5.873 10.94,4.325 8.178,4.325C5.882,4.325 3.838,5.189 2.367,6.809C0.897,8.43 0,10.842 0,13.975V14.264C0,17.396 0.897,19.809 2.331,21.43C3.802,23.122 5.847,23.986 8.178,23.986C11.083,23.986 13.2,22.546 14.168,20.709V23.588H19.369V4.757ZM9.469,19.917C6.6,19.917 5.022,18.008 5.022,14.299V14.011C5.022,10.158 6.887,8.322 9.505,8.322C12.231,8.322 14.097,10.23 14.097,14.011V14.299C14.097,18.08 12.339,19.917 9.469,19.917ZM24.411,4.757H29.612V7.746C30.581,5.873 32.554,4.325 35.71,4.325C37.575,4.325 39.118,4.901 40.265,6.053C41.377,7.241 42.023,9.114 42.023,11.599V23.59H36.822V12.355C36.822,9.762 35.818,8.574 33.522,8.574C31.262,8.574 29.612,9.942 29.612,12.715V23.59H24.411V4.757ZM51.248,21.681L43.787,4.756H49.382L53.938,16.064L58.134,4.756H63.12L52.969,30H47.948L51.248,21.681ZM71.872,23.95C67.891,23.95 65.81,21.934 65.81,18.008V0H70.976V4.757H75.616V8.466H70.976V17.54C70.976,19.017 71.657,19.773 73.056,19.773C74.414,19.773 75.055,19.445 75.616,19.137V23.129C74.634,23.6 73.618,23.95 71.872,23.95ZM85.379,21.681L77.919,4.756H83.514L88.07,16.064L92.266,4.756H97.252L87.101,30H82.079L85.379,21.681ZM99.59,4.757H104.791V7.71C105.831,5.873 108.019,4.325 110.781,4.325C113.077,4.325 115.121,5.189 116.592,6.809C118.062,8.43 118.959,10.842 118.959,13.975V14.264C118.959,17.396 118.062,19.809 116.628,21.43C115.157,23.122 113.112,23.986 110.781,23.986C107.876,23.986 105.759,22.546 104.791,20.709V30H99.59V4.757ZM109.171,19.917C112.041,19.917 113.619,18.008 113.619,14.299V14.011C113.619,10.158 111.754,8.322 109.135,8.322C106.409,8.322 104.544,10.23 104.544,14.011V14.299C104.544,18.08 106.302,19.917 109.171,19.917ZM130.997,23.982C128.164,23.982 125.725,23.118 124.003,21.425C122.246,19.805 121.206,17.428 121.206,14.403V14.115C121.206,11.127 122.246,8.714 124.003,6.986C125.725,5.293 128.128,4.321 130.854,4.321C133.293,4.321 135.553,5.041 137.238,6.59C138.924,8.138 140,10.551 140,13.899V15.34H126.478C126.622,18.508 128.272,20.273 131.141,20.273C133.58,20.273 134.728,19.229 135.05,17.644H139.964C139.354,21.713 136.162,23.982 130.997,23.982ZM134.943,12.134C134.764,9.254 133.329,7.922 130.854,7.922C128.523,7.922 126.945,9.434 126.55,12.134H134.943Z"
|
||||
android:fillColor="#2AA7EE"
|
||||
android:fillAlpha="0.6"
|
||||
android:pathData="M16.03,20.99V4.07H12.09V6.88H11.86C11.65,6.47 11.36,6.03 10.99,5.56C10.61,5.09 10.1,4.69 9.46,4.36C8.82,4.02 8,3.85 7.01,3.85C5.7,3.85 4.51,4.18 3.46,4.86C2.4,5.52 1.56,6.5 0.94,7.81C0.31,9.1 0,10.69 0,12.58C0,14.44 0.31,16.03 0.92,17.33C1.53,18.64 2.36,19.63 3.42,20.32C4.47,21 5.66,21.34 7,21.34C7.97,21.34 8.78,21.18 9.42,20.86C10.06,20.53 10.57,20.14 10.96,19.69C11.35,19.22 11.65,18.78 11.86,18.37H12.02V20.99H16.03ZM12.1,12.56C12.1,13.66 11.95,14.62 11.64,15.44C11.32,16.27 10.87,16.91 10.28,17.38C9.68,17.83 8.96,18.06 8.11,18.06C7.23,18.06 6.49,17.83 5.89,17.35C5.29,16.88 4.84,16.22 4.54,15.4C4.23,14.57 4.08,13.62 4.08,12.56C4.08,11.5 4.23,10.57 4.53,9.75C4.83,8.93 5.28,8.29 5.88,7.83C6.48,7.36 7.22,7.13 8.11,7.13C8.97,7.13 9.69,7.36 10.29,7.81C10.89,8.26 11.34,8.89 11.65,9.7C11.95,10.51 12.1,11.46 12.1,12.56Z"
|
||||
android:fillColor="#DBDAD4"/>
|
||||
<path
|
||||
android:pathData="M64.59,4.07V7.16H61.24V15.94C61.24,16.47 61.32,16.89 61.48,17.17C61.65,17.45 61.87,17.65 62.14,17.75C62.42,17.85 62.72,17.9 63.05,17.9C63.3,17.9 63.53,17.89 63.73,17.85C63.95,17.81 64.11,17.78 64.22,17.75L64.89,20.88C64.68,20.95 64.38,21.03 63.98,21.12C63.59,21.21 63.11,21.26 62.54,21.27C61.54,21.3 60.64,21.15 59.83,20.82C59.03,20.48 58.39,19.96 57.92,19.25C57.46,18.54 57.23,17.66 57.24,16.6V7.16V4.07V0H61.24V4.07H64.59ZM23.82,21.04V11.1C23.82,10.28 23.97,9.58 24.27,9.01C24.58,8.43 25.01,7.99 25.54,7.69C26.09,7.38 26.71,7.23 27.41,7.23C28.44,7.23 29.25,7.55 29.83,8.19C30.42,8.83 30.72,9.72 30.72,10.85V21.04H34.72V10.23C34.72,8.88 34.49,7.73 34,6.77C33.52,5.82 32.84,5.1 31.97,4.6C31.1,4.1 30.08,3.85 28.9,3.85C27.64,3.85 26.57,4.13 25.71,4.69C24.86,5.25 24.23,6 23.84,6.95H23.64V4.07H19.82V21.04H23.82ZM70.58,27.23C71.3,25.29 72.01,23.34 72.74,21.4L66.58,4.07H70.83L74.74,16.89H74.92L78.84,4.07H83.09L74.8,27.23H70.58ZM85.86,27.41V4.07H89.8V6.87H90.03C90.24,6.46 90.53,6.02 90.9,5.56C91.28,5.09 91.79,4.69 92.43,4.35C93.07,4.02 93.89,3.85 94.88,3.85C96.19,3.85 97.37,4.18 98.43,4.85C99.49,5.51 100.33,6.5 100.95,7.8C101.57,9.1 101.89,10.69 101.89,12.58C101.89,14.44 101.58,16.03 100.97,17.33C100.36,18.63 99.53,19.63 98.47,20.31C97.42,21 96.23,21.34 94.89,21.34C93.92,21.34 93.11,21.18 92.47,20.85C91.83,20.53 91.31,20.14 90.92,19.68C90.54,19.22 90.24,18.78 90.03,18.37H89.86V27.41H85.86ZM89.79,12.55C89.79,13.65 89.94,14.61 90.25,15.44C90.57,16.26 91.02,16.91 91.61,17.37C92.21,17.83 92.93,18.06 93.78,18.06C94.66,18.06 95.4,17.82 96,17.35C96.59,16.87 97.04,16.22 97.35,15.4C97.65,14.56 97.81,13.62 97.81,12.55C97.81,11.5 97.66,10.57 97.36,9.75C97.05,8.93 96.6,8.29 96.01,7.82C95.41,7.36 94.67,7.13 93.78,7.13C92.92,7.13 92.2,7.35 91.6,7.8C91,8.25 90.55,8.88 90.24,9.69C89.94,10.5 89.79,11.46 89.79,12.55ZM108.42,20.31C109.66,21.02 111.13,21.37 112.83,21.37C114.15,21.37 115.32,21.18 116.34,20.78C117.37,20.37 118.21,19.8 118.86,19.08C119.53,18.34 119.98,17.48 120.21,16.49L116.48,16.07C116.3,16.55 116.04,16.95 115.69,17.27C115.35,17.6 114.94,17.84 114.46,18.01C113.99,18.18 113.47,18.26 112.88,18.26C112.01,18.26 111.26,18.07 110.61,17.7C109.96,17.33 109.45,16.79 109.09,16.09C108.75,15.41 108.56,14.59 108.55,13.65H120.38V12.42C120.38,10.93 120.17,9.65 119.76,8.58C119.35,7.49 118.78,6.6 118.06,5.9C117.34,5.2 116.52,4.69 115.58,4.35C114.65,4.02 113.67,3.85 112.62,3.85C111,3.85 109.59,4.22 108.39,4.96C107.18,5.7 106.25,6.73 105.58,8.06C104.92,9.38 104.58,10.91 104.58,12.65C104.58,14.43 104.92,15.97 105.58,17.28C106.24,18.59 107.19,19.6 108.42,20.31ZM108.56,10.95C108.6,10.29 108.77,9.67 109.07,9.11C109.42,8.47 109.9,7.95 110.52,7.56C111.14,7.16 111.86,6.96 112.67,6.96C113.44,6.96 114.11,7.14 114.69,7.48C115.27,7.83 115.72,8.3 116.04,8.91C116.37,9.5 116.54,10.19 116.54,10.95H108.56Z"
|
||||
android:fillColor="#DBDAD4"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M66.58,4.07L72.74,21.4C72.01,23.34 71.3,25.29 70.58,27.23H74.8L83.09,4.07H78.84L74.92,16.89H74.74L70.83,4.07H66.58Z"
|
||||
android:fillColor="#DBDAD4"/>
|
||||
<path
|
||||
android:pathData="M36.98,4.07L43.14,21.4C42.41,23.34 41.69,25.28 40.97,27.23H45.2L53.49,4.07H49.24L45.31,16.89H45.14L41.22,4.07H36.98Z"
|
||||
android:fillColor="#DBDAD4"/>
|
||||
</vector>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_primary">
|
||||
android:background="@android:color/black">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logo"
|
||||
|
@ -19,7 +19,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textColor="@color/palette_system_sky"
|
||||
android:textColor="#DBDAD4"
|
||||
android:textSize="10sp"
|
||||
tools:text="0.0.10-alpha" />
|
||||
|
||||
<TextView
|
||||
|
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_new_app_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_new_app_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_new_app_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_new_app_launcher_foreground"/>
|
||||
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 4.8 KiB |
|
@ -337,9 +337,6 @@
|
|||
android:id="@+id/action_openHome"
|
||||
app:destination="@id/homeScreen"
|
||||
app:enterAnim="@anim/fade_in"
|
||||
app:exitAnim="@anim/fade_out"
|
||||
app:popEnterAnim="@anim/fade_in"
|
||||
app:popExitAnim="@anim/fade_out"
|
||||
app:popUpTo="@id/authStartScreen"
|
||||
app:popUpToInclusive="true" />
|
||||
|
||||
|
@ -535,7 +532,7 @@
|
|||
<dialog
|
||||
android:id="@+id/relationCreationFragment"
|
||||
android:name="com.anytypeio.anytype.ui.relations.RelationCreateFromLibraryFragment"
|
||||
android:label="RelationCreationFragment"></dialog>
|
||||
android:label="RelationCreationFragment"/>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/typeEditingFragment"
|
||||
|
@ -666,15 +663,6 @@
|
|||
android:id="@+id/migrationNeededScreen"
|
||||
android:name="com.anytypeio.anytype.ui.update.MigrationErrorFragment"
|
||||
android:label="Migration-needed screen">
|
||||
<action
|
||||
android:id="@+id/action_splashFragment_to_login_nav"
|
||||
app:destination="@id/login_nav"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim"
|
||||
app:popUpTo="@+id/splashScreen"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_new_app_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
|
@ -212,7 +212,7 @@ Do the computation of an expensive paragraph of text on a background thread:
|
|||
<string name="btn_upload_storage">Upload from External storage</string>
|
||||
<string name="btn_upload">Upload</string>
|
||||
|
||||
<string name="alert_qr_camera">Open "Profile" and then "Recovery phrase" on Anytype Desktop.</string>
|
||||
<string name="alert_qr_camera">Go to Settings – Recovery Phrase on Desktop in order to find the QR code for login.</string>
|
||||
<string name="alert_qr_camera_ok">Ok</string>
|
||||
<string name="choose_option">Choose option</string>
|
||||
<string name="find_a_relation">Find a relation</string>
|
||||
|
@ -391,4 +391,7 @@ Do the computation of an expensive paragraph of text on a background thread:
|
|||
<string name="mail_more_space_body">Hi, Anytype team. I am reaching out to request an increase in my file storage capacity as I have run out of storage. My current limits is %1$s. My account id is %2$s. Cheers, %3$s</string>
|
||||
<string name="your_recovery_phrase_can_t_be_empty">Your recovery phrase can\'t be empty</string>
|
||||
<string name="go_to_the_app">Go to the app</string>
|
||||
|
||||
<string name="exiting_please_wait">Exiting... please wait</string>
|
||||
<string name="loading_please_wait">Loading... please wait</string>
|
||||
</resources>
|
||||
|
|
|
@ -43,7 +43,6 @@ plugins {
|
|||
alias(libs.plugins.wire) apply false
|
||||
alias(libs.plugins.dokka) apply false
|
||||
alias(libs.plugins.firebaseDistribution) apply false
|
||||
alias(libs.plugins.crashlytics) apply false
|
||||
alias(libs.plugins.navigation) apply false
|
||||
alias(libs.plugins.gms) apply false
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ typealias Key = String
|
|||
typealias Url = String
|
||||
typealias Hash = String
|
||||
typealias Struct = Map<Id, Any?>
|
||||
typealias Name = String
|
||||
typealias Filepath = String
|
||||
|
||||
typealias Document = List<Block>
|
||||
|
||||
|
|
|
@ -323,7 +323,8 @@ data class Block(
|
|||
val id: Id = "",
|
||||
val relationKey: String,
|
||||
val type: Type,
|
||||
val includeTime: Boolean = false
|
||||
val includeTime: Boolean = false,
|
||||
val customOrder: List<Id> = emptyList()
|
||||
) {
|
||||
enum class Type(val formattedName: String) {
|
||||
ASC("ascending"),
|
||||
|
|
|
@ -23,7 +23,7 @@ sealed class ObjectWrapper {
|
|||
val name: String? by default
|
||||
|
||||
val iconEmoji: String? by default
|
||||
val iconImage: String? by default
|
||||
val iconImage: String? = getValue(Relations.ICON_IMAGE)
|
||||
val iconOption: Double? by default
|
||||
|
||||
val coverId: String? by default
|
||||
|
@ -135,7 +135,7 @@ sealed class ObjectWrapper {
|
|||
val description: String? by default
|
||||
val source: String? by default
|
||||
val iconEmoji: String? by default
|
||||
val iconImage: String? by default
|
||||
val iconImage: String? = getValue(Relations.ICON_IMAGE)
|
||||
val picture: String? by default
|
||||
val isArchived: Boolean? by default
|
||||
val isDeleted: Boolean? by default
|
||||
|
|
|
@ -11,7 +11,7 @@ android {
|
|||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion libs.versions.androidxComposeVersion1.get()
|
||||
kotlinCompilerExtensionVersion libs.versions.composeKotlinCompilerVersion.get()
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
|
|
@ -17,6 +17,8 @@ val ColorButtonSecondaryPressed = Color(0xFF1F1E1C)
|
|||
val ColorButtonSecondaryBorder = Color(0xFF525148)
|
||||
val ColorButtonSecondaryBorderPressed = Color(0xFF525148)
|
||||
val ColorButtonSecondaryText = Color(0xFF7B7A72)
|
||||
val ColorButtonRegular = Color(0xFFCBC9BD)
|
||||
val ColorButtonInversion= Color(0xFFFFFFFF)
|
||||
|
||||
val ColorPagerIndicator = Color(0xFF3A3935)
|
||||
val ColorPagerIndicatorCurrent = Color(0xFFF3F2EC)
|
||||
|
@ -30,4 +32,17 @@ val ColorBackgroundField = Color(0x26DAD7CA)
|
|||
val ColorMnemonicStub = Color(0xFF252525)
|
||||
val ColorPlaceholderText = Color(0x4DFFFFFF)
|
||||
val ColorSoulConnectorLine = Color(0x30DBDAD4)
|
||||
val ColorSpaceBackground = Color(0xFFDBDAD4)
|
||||
val ColorSpaceBackground = Color(0xFFDBDAD4)
|
||||
|
||||
|
||||
val MnemonicPhrasePaletteColors = listOf(
|
||||
Color(0xFFF1A91A),
|
||||
Color(0xFFF55522),
|
||||
Color(0xFF5871FF),
|
||||
Color(0xFF18A3F1),
|
||||
Color(0xFFDD2EA1),
|
||||
Color(0xFFB354D7),
|
||||
Color(0xFF5DD400),
|
||||
)
|
||||
|
||||
const val MNEMONIC_WORD_COUNT = 12
|
|
@ -87,7 +87,7 @@ class BlockViewDiffUtil(
|
|||
if (newBlock is Focusable && oldBlock is Focusable) {
|
||||
if (newBlock.isFocused != oldBlock.isFocused) {
|
||||
changes.add(FOCUS_CHANGED)
|
||||
Timber.d("Focus changed!")
|
||||
Timber.d("Focus changed to [${newBlock.isFocused}] for block ${newBlock.id}")
|
||||
} else
|
||||
Timber.d("Focus hasn't changed")
|
||||
}
|
||||
|
|
|
@ -6,13 +6,10 @@ import android.text.style.LeadingMarginSpan
|
|||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_ui.BuildConfig
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.SearchHighlightSpan
|
||||
import com.anytypeio.anytype.core_ui.common.SearchTargetHighlightSpan
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkBinding
|
||||
import com.anytypeio.anytype.core_ui.extensions.setBlockBackgroundColor
|
||||
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
|
||||
import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder
|
||||
import com.anytypeio.anytype.core_ui.features.editor.EditorTouchProcessor
|
||||
|
@ -22,7 +19,6 @@ import com.anytypeio.anytype.core_utils.ext.dimen
|
|||
import com.anytypeio.anytype.core_utils.ext.gone
|
||||
import com.anytypeio.anytype.core_utils.ext.removeSpans
|
||||
import com.anytypeio.anytype.core_utils.ext.visible
|
||||
import com.anytypeio.anytype.core_models.ThemeColor
|
||||
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Searchable.Field.Companion.DEFAULT_SEARCH_FIELD_KEY
|
||||
|
@ -41,7 +37,7 @@ class LinkToObject(
|
|||
private val untitled = itemView.resources.getString(R.string.untitled)
|
||||
val objectIcon = binding.objectIconWidget
|
||||
private val objectIconContainer = binding.iconObjectContainer
|
||||
private val title = binding.text
|
||||
val title = binding.text
|
||||
private val description = binding.tvDescription
|
||||
private val objectType = binding.tvObjectType
|
||||
|
||||
|
@ -63,8 +59,8 @@ class LinkToObject(
|
|||
applyText(item)
|
||||
applyDescription(item)
|
||||
applyObjectType(item)
|
||||
applySearchHighlight(item)
|
||||
applyImageOrEmoji(item)
|
||||
applySearchHighlight(item)
|
||||
itemView.setOnClickListener { clicked(ListenerType.LinkToObject(item.id)) }
|
||||
}
|
||||
|
||||
|
@ -89,7 +85,7 @@ class LinkToObject(
|
|||
)
|
||||
}
|
||||
title.visible()
|
||||
title.text = sb
|
||||
title.setText(sb, TextView.BufferType.EDITABLE)
|
||||
}
|
||||
else -> {
|
||||
val sb = SpannableString(name)
|
||||
|
@ -101,7 +97,7 @@ class LinkToObject(
|
|||
)
|
||||
}
|
||||
title.visible()
|
||||
title.text = sb
|
||||
title.setText(sb, TextView.BufferType.EDITABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,9 +177,6 @@ class LinkToObject(
|
|||
if (payload.isSelectionChanged) {
|
||||
applySelectedState(item)
|
||||
}
|
||||
if (payload.isSearchHighlightChanged) {
|
||||
applySearchHighlight(item)
|
||||
}
|
||||
if (payload.isObjectTitleChanged) {
|
||||
applyText(item)
|
||||
}
|
||||
|
@ -196,6 +189,9 @@ class LinkToObject(
|
|||
if (payload.isObjectTypeChanged) {
|
||||
applyObjectType(item)
|
||||
}
|
||||
if (payload.isSearchHighlightChanged) {
|
||||
applySearchHighlight(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class LinkToObjectArchive(
|
|||
|
||||
private val root = binding.root
|
||||
private val untitled = itemView.resources.getString(R.string.untitled)
|
||||
private val title = binding.pageTitle
|
||||
val title = binding.pageTitle
|
||||
|
||||
override val editorTouchProcessor = EditorTouchProcessor(
|
||||
fallback = { e -> itemView.onTouchEvent(e) }
|
||||
|
|
|
@ -77,12 +77,12 @@ abstract class LinkToObjectCard(
|
|||
|
||||
applyBackground(item.background)
|
||||
|
||||
applySearchHighlight(item)
|
||||
|
||||
applyImageOrEmoji(item)
|
||||
|
||||
applyObjectType(item)
|
||||
|
||||
applySearchHighlight(item)
|
||||
|
||||
itemView.setOnClickListener { clicked(ListenerType.LinkToObject(item.id)) }
|
||||
}
|
||||
|
||||
|
@ -97,12 +97,12 @@ abstract class LinkToObjectCard(
|
|||
name.isBlank() -> {
|
||||
titleView.visible()
|
||||
val sb = SpannableString(untitled)
|
||||
titleView.setText(sb, TextView.BufferType.SPANNABLE)
|
||||
titleView.setText(sb, TextView.BufferType.EDITABLE)
|
||||
}
|
||||
else -> {
|
||||
titleView.visible()
|
||||
val sb = SpannableString(name)
|
||||
titleView.setText(sb, TextView.BufferType.SPANNABLE)
|
||||
titleView.setText(sb, TextView.BufferType.EDITABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,20 @@ open class MentionFooterItemDecorator(private val screen: Point) : RecyclerView.
|
|||
|
||||
class MarkupColorToolbarFooter(screen: Point) : MentionFooterItemDecorator(screen)
|
||||
class SlashWidgetFooterItemDecorator(screen: Point) : MentionFooterItemDecorator(screen)
|
||||
class StyleToolbarItemDecorator(screen: Point) : MentionFooterItemDecorator(screen)
|
||||
class StyleToolbarItemDecorator(screen: Point) : MentionFooterItemDecorator(screen)
|
||||
|
||||
|
||||
class LastItemBottomOffsetDecorator(private val offset: Int) : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
val adapter = parent.adapter ?: return
|
||||
val pos = parent.getChildAdapterPosition(view)
|
||||
if (pos == adapter.itemCount - 1) {
|
||||
outRect.bottom = offset
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,8 +33,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
|
||||
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
|
||||
import com.anytypeio.anytype.core_ui.views.animations.LoadingIndicator
|
||||
|
||||
class ButtonPrimaryXSmall @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -154,6 +154,45 @@ fun ButtonPrimary(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ButtonPrimaryDarkTheme(
|
||||
text: String = "",
|
||||
onClick: () -> Unit,
|
||||
enabled: Boolean = true,
|
||||
modifier: Modifier = Modifier,
|
||||
size: ButtonSize
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val backgroundColor = Color(0xFFF3F2EC)
|
||||
|
||||
CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
interactionSource = interactionSource,
|
||||
enabled = enabled,
|
||||
shape = RoundedCornerShape(size.cornerSize),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = backgroundColor,
|
||||
contentColor = Color(0xFF1F1E1D),
|
||||
disabledBackgroundColor = Color(0xFF1F1E1D),
|
||||
disabledContentColor = Color(0xFF1F1E1D)
|
||||
),
|
||||
modifier = modifier
|
||||
.defaultMinSize(minWidth = 1.dp, minHeight = 1.dp),
|
||||
elevation = ButtonDefaults.elevation(
|
||||
defaultElevation = 0.dp,
|
||||
pressedElevation = 0.dp
|
||||
),
|
||||
contentPadding = size.contentPadding
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = size.textStyle
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ButtonPrimaryLoading(
|
||||
text: String = "",
|
||||
|
@ -202,7 +241,7 @@ fun ButtonPrimaryLoading(
|
|||
style = size.textStyle
|
||||
)
|
||||
}
|
||||
LoadingIndicator(
|
||||
DotsLoadingIndicator(
|
||||
animating = loading,
|
||||
modifier = Modifier.graphicsLayer { alpha = loadingAlpha },
|
||||
animationSpecs = FadeAnimationSpecs(itemCount = loadingItemsCount),
|
||||
|
@ -309,7 +348,7 @@ fun ButtonSecondaryLoading(
|
|||
modifier = Modifier.graphicsLayer { alpha = contentAlpha }
|
||||
)
|
||||
}
|
||||
LoadingIndicator(
|
||||
DotsLoadingIndicator(
|
||||
animating = loading,
|
||||
modifier = Modifier.graphicsLayer { alpha = loadingAlpha },
|
||||
animationSpecs = FadeAnimationSpecs(itemCount = loadingItemsCount),
|
||||
|
@ -407,7 +446,7 @@ fun ButtonWarningLoading(
|
|||
style = size.textStyle
|
||||
)
|
||||
}
|
||||
LoadingIndicator(
|
||||
DotsLoadingIndicator(
|
||||
animating = loading,
|
||||
modifier = Modifier.graphicsLayer { alpha = loadingAlpha },
|
||||
animationSpecs = FadeAnimationSpecs(itemCount = loadingItemsCount),
|
||||
|
@ -487,6 +526,19 @@ fun MyPrimaryButton() {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun MyPrimaryButtonDark() {
|
||||
ButtonPrimaryDarkTheme(
|
||||
onClick = {},
|
||||
size = ButtonSize.Large,
|
||||
text = "Login",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun MySecondaryButton() {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package com.anytypeio.anytype.core_ui.views
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@ExperimentalMaterial3Api
|
||||
@Composable
|
||||
fun BaseAlertDialog(
|
||||
dialogText: String,
|
||||
buttonText: String,
|
||||
onButtonClick: () -> Unit,
|
||||
onDismissRequest: () -> Unit
|
||||
): Unit {
|
||||
val modifier = Modifier
|
||||
.shadow(
|
||||
elevation = 40.dp, spotColor = Color(0x40000000), ambientColor = Color(0x40000000)
|
||||
)
|
||||
.wrapContentWidth()
|
||||
.wrapContentHeight()
|
||||
.background(
|
||||
color = Color(0xFF1F1E1D), shape = RoundedCornerShape(size = 8.dp)
|
||||
)
|
||||
.padding(start = 32.dp, top = 32.dp, end = 32.dp, bottom = 32.dp)
|
||||
|
||||
AlertDialog(onDismissRequest = onDismissRequest) {
|
||||
Surface(
|
||||
modifier = modifier,
|
||||
shape = MaterialTheme.shapes.large,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation,
|
||||
color = Color.Transparent
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = dialogText,
|
||||
style = UXBody,
|
||||
textAlign = TextAlign.Center,
|
||||
color = Color(0xFFFFFFFF),
|
||||
modifier = Modifier.padding(horizontal = 10.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(18.dp))
|
||||
ButtonPrimaryDarkTheme(
|
||||
text = buttonText,
|
||||
onClick = onButtonClick,
|
||||
size = ButtonSize.Large,
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonPrimaryActive
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonPrimaryInactive
|
||||
|
@ -26,7 +25,6 @@ import com.anytypeio.anytype.core_ui.ColorButtonSecondaryBorder
|
|||
import com.anytypeio.anytype.core_ui.ColorButtonSecondaryBorderPressed
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonSecondaryPressed
|
||||
import com.anytypeio.anytype.core_ui.ColorButtonSecondaryText
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
|
||||
@Composable
|
||||
fun OnBoardingButtonPrimary(
|
||||
|
@ -78,7 +76,8 @@ fun OnBoardingButtonSecondary(
|
|||
onClick: () -> Unit,
|
||||
enabled: Boolean = true,
|
||||
modifier: Modifier = Modifier,
|
||||
size: ButtonSize
|
||||
size: ButtonSize,
|
||||
textColor: Color = ColorButtonSecondaryText
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed = interactionSource.collectIsPressedAsState()
|
||||
|
@ -114,7 +113,7 @@ fun OnBoardingButtonSecondary(
|
|||
Text(
|
||||
text = text,
|
||||
style = size.textStyle.copy(
|
||||
color = ColorButtonSecondaryText
|
||||
color = textColor
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ fun rememberLoadingIndicatorState(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun LoadingIndicator(
|
||||
fun DotsLoadingIndicator(
|
||||
animating: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.White,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="true"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
|
|
|
@ -991,8 +991,8 @@ class BlockAdapterTest {
|
|||
check(firstHolder is Paragraph)
|
||||
check(secondHolder is Paragraph)
|
||||
|
||||
assertTrue { firstHolder.content.isSelected }
|
||||
assertFalse { secondHolder.content.isSelected }
|
||||
assertTrue { firstHolder.selectionView.isSelected }
|
||||
assertFalse { secondHolder.selectionView.isSelected }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1369,12 +1369,12 @@ class BlockAdapterTest {
|
|||
check(h3SelectedHolder is HeaderThree)
|
||||
check(h3NotSelectedHolder is HeaderThree)
|
||||
|
||||
assertTrue { h1SelectedHolder.content.isSelected }
|
||||
assertFalse { h1NotSelectedHolder.content.isSelected }
|
||||
assertTrue { h2SelectedHolder.content.isSelected }
|
||||
assertFalse { h2NotSelectedHolder.content.isSelected }
|
||||
assertTrue { h3SelectedHolder.content.isSelected }
|
||||
assertFalse { h3NotSelectedHolder.content.isSelected }
|
||||
assertTrue { h1SelectedHolder.selectionView.isSelected }
|
||||
assertFalse { h1NotSelectedHolder.selectionView.isSelected }
|
||||
assertTrue { h2SelectedHolder.selectionView.isSelected }
|
||||
assertFalse { h2NotSelectedHolder.selectionView.isSelected }
|
||||
assertTrue { h3SelectedHolder.selectionView.isSelected }
|
||||
assertFalse { h3NotSelectedHolder.selectionView.isSelected }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
package com.anytypeio.anytype.core_ui.features.editor.holders.other
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.testing.FragmentScenario
|
||||
import androidx.fragment.app.testing.launchFragmentInContainer
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.anytypeio.anytype.core_models.ThemeColor
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.SearchHighlightSpan
|
||||
import com.anytypeio.anytype.core_ui.common.SearchTargetHighlightSpan
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkArchiveBinding
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkBinding
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardMediumIconBinding
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardMediumIconCoverBinding
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardSmallIconBinding
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardSmallIconCoverBinding
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.TestFragment
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.core.IsEqual.equalTo
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(
|
||||
manifest = Config.NONE,
|
||||
sdk = [Build.VERSION_CODES.P],
|
||||
instrumentedPackages = [
|
||||
"androidx.loader.content"
|
||||
]
|
||||
)
|
||||
class LinkToObjectTest {
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
lateinit var scenario: FragmentScenario<TestFragment>
|
||||
|
||||
private lateinit var linkToObject: LinkToObject
|
||||
private lateinit var linkToObjectArchived: LinkToObjectArchive
|
||||
private lateinit var linkToObjectCardSmallIcon: LinkToObjectCardSmallIcon
|
||||
private lateinit var linkToObjectCardSmallIconCover: LinkToObjectCardSmallIconCover
|
||||
private lateinit var linkToObjectCardMediumIcon: LinkToObjectCardMediumIcon
|
||||
private lateinit var linkToObjectCardMediumIconCover: LinkToObjectCardMediumIconCover
|
||||
|
||||
private lateinit var item: BlockView.LinkToObject.Default.Text
|
||||
private lateinit var itemArchived: BlockView.LinkToObject.Archived
|
||||
private lateinit var itemCardSmallIcon: BlockView.LinkToObject.Default.Card.SmallIcon
|
||||
private lateinit var itemCardSmallIconCover: BlockView.LinkToObject.Default.Card.SmallIconCover
|
||||
private lateinit var itemCardMediumIcon: BlockView.LinkToObject.Default.Card.MediumIcon
|
||||
private lateinit var itemCardMediumIconCover: BlockView.LinkToObject.Default.Card.MediumIconCover
|
||||
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context.setTheme(R.style.Theme_MaterialComponents)
|
||||
scenario = launchFragmentInContainer()
|
||||
|
||||
val layoutInflater = LayoutInflater.from(context)
|
||||
linkToObject = LinkToObject(ItemBlockObjectLinkBinding.inflate(layoutInflater))
|
||||
linkToObjectArchived = LinkToObjectArchive(ItemBlockObjectLinkArchiveBinding.inflate(layoutInflater))
|
||||
linkToObjectCardSmallIcon = LinkToObjectCardSmallIcon(ItemBlockObjectLinkCardSmallIconBinding.inflate(layoutInflater))
|
||||
linkToObjectCardSmallIconCover = LinkToObjectCardSmallIconCover(
|
||||
ItemBlockObjectLinkCardSmallIconCoverBinding.inflate(layoutInflater))
|
||||
linkToObjectCardMediumIcon = LinkToObjectCardMediumIcon(
|
||||
ItemBlockObjectLinkCardMediumIconBinding.inflate(layoutInflater))
|
||||
linkToObjectCardMediumIconCover = LinkToObjectCardMediumIconCover(
|
||||
ItemBlockObjectLinkCardMediumIconCoverBinding.inflate(layoutInflater))
|
||||
|
||||
val id = MockDataFactory.randomUuid()
|
||||
|
||||
val searchField = BlockView.Searchable.Field(
|
||||
highlights = listOf(IntRange(0, 4), IntRange(6, 10)),
|
||||
target = IntRange(0, 4)
|
||||
)
|
||||
|
||||
item = BlockView.LinkToObject.Default.Text(
|
||||
id = id,
|
||||
indent = MockDataFactory.randomInt(),
|
||||
isSelected = false,
|
||||
icon = ObjectIcon.None,
|
||||
searchFields = listOf(searchField),
|
||||
text = "Text1 Text2"
|
||||
)
|
||||
|
||||
itemArchived = BlockView.LinkToObject.Archived(
|
||||
id = id,
|
||||
indent = MockDataFactory.randomInt(),
|
||||
isSelected = false,
|
||||
searchFields = listOf(searchField),
|
||||
text = "Text1 Text2"
|
||||
)
|
||||
|
||||
itemCardSmallIcon = BlockView.LinkToObject.Default.Card.SmallIcon(
|
||||
id = id,
|
||||
indent = MockDataFactory.randomInt(),
|
||||
isSelected = false,
|
||||
icon = ObjectIcon.None,
|
||||
searchFields = listOf(searchField),
|
||||
text = "Text1 Text2",
|
||||
background = ThemeColor.BLUE,
|
||||
isPreviousBlockMedia = false
|
||||
)
|
||||
|
||||
itemCardSmallIconCover = BlockView.LinkToObject.Default.Card.SmallIconCover(
|
||||
id = id,
|
||||
indent = MockDataFactory.randomInt(),
|
||||
isSelected = false,
|
||||
icon = ObjectIcon.None,
|
||||
searchFields = listOf(searchField),
|
||||
text = "Text1 Text2",
|
||||
background = ThemeColor.BLUE,
|
||||
isPreviousBlockMedia = false,
|
||||
cover = null
|
||||
)
|
||||
|
||||
itemCardMediumIcon = BlockView.LinkToObject.Default.Card.MediumIcon(
|
||||
id = id,
|
||||
indent = MockDataFactory.randomInt(),
|
||||
isSelected = false,
|
||||
icon = ObjectIcon.None,
|
||||
searchFields = listOf(searchField),
|
||||
text = "Text1 Text2",
|
||||
background = ThemeColor.BLUE,
|
||||
isPreviousBlockMedia = false
|
||||
)
|
||||
|
||||
itemCardMediumIconCover = BlockView.LinkToObject.Default.Card.MediumIconCover(
|
||||
id = id,
|
||||
indent = MockDataFactory.randomInt(),
|
||||
isSelected = false,
|
||||
icon = ObjectIcon.None,
|
||||
searchFields = listOf(searchField),
|
||||
text = "Text1 Text2",
|
||||
background = ThemeColor.BLUE,
|
||||
isPreviousBlockMedia = false,
|
||||
cover = null
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test linkToObject block`() {
|
||||
linkToObject.bind(item, {})
|
||||
assertSearchSpans(linkToObject.title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test linkToObjectArchive block`() {
|
||||
linkToObjectArchived.bind(itemArchived, {})
|
||||
assertSearchSpans(linkToObjectArchived.title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test linkToObjectCard block`() {
|
||||
linkToObjectCardSmallIcon.bind(itemCardSmallIcon, {})
|
||||
assertSearchSpans(linkToObjectCardSmallIcon.titleView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test linkToObjectCardCover block`() {
|
||||
linkToObjectCardSmallIconCover.bind(itemCardSmallIconCover, {})
|
||||
assertSearchSpans(linkToObjectCardSmallIconCover.titleView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test linkToObjectCardMediumIcon block`() {
|
||||
linkToObjectCardMediumIcon.bind(itemCardMediumIcon, {})
|
||||
assertSearchSpans(linkToObjectCardMediumIcon.titleView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test linkToObjectCardMediumIconCover block`() {
|
||||
linkToObjectCardMediumIconCover.bind(itemCardMediumIconCover, {})
|
||||
assertSearchSpans(linkToObjectCardMediumIconCover.titleView)
|
||||
}
|
||||
|
||||
private fun assertSearchSpans(textView: TextView) {
|
||||
// verify
|
||||
val spans = textView.editableText.getSpans(0, item.text!!.length, SearchHighlightSpan::class.java)
|
||||
assertThat(spans.size, equalTo(2))
|
||||
assertThat(textView.editableText.getSpanStart(spans[0]), equalTo(0))
|
||||
assertThat(textView.editableText.getSpanEnd(spans[0]), equalTo(4))
|
||||
assertThat(textView.editableText.getSpanStart(spans[1]), equalTo(6))
|
||||
assertThat(textView.editableText.getSpanEnd(spans[1]), equalTo(10))
|
||||
|
||||
val targetSpans = textView.editableText.getSpans(0, textView.text.length, SearchTargetHighlightSpan::class.java)
|
||||
assertThat(targetSpans.size, equalTo(1))
|
||||
assertThat(textView.editableText.getSpanStart(targetSpans[0]), equalTo(0))
|
||||
assertThat(textView.editableText.getSpanEnd(targetSpans[0]), equalTo(4))
|
||||
}
|
||||
}
|
|
@ -228,8 +228,7 @@ class EditorDecorationContainerTest {
|
|||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_highlight_content_margin_top)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = rectWithVerticalQuoteLine.marginBottom
|
||||
)
|
||||
|
||||
|
@ -293,8 +292,7 @@ class EditorDecorationContainerTest {
|
|||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_highlight_content_margin_top)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = rectWithVerticalQuoteLine.marginBottom
|
||||
)
|
||||
|
||||
|
@ -355,8 +353,7 @@ class EditorDecorationContainerTest {
|
|||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_highlight_content_margin_top)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = rectWithVerticalQuoteLine.marginBottom
|
||||
)
|
||||
|
||||
|
@ -919,14 +916,12 @@ class EditorDecorationContainerTest {
|
|||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_header_two_extra_space_top)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = view2.marginTop
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_header_two_extra_space_bottom)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = view2.marginBottom
|
||||
)
|
||||
}
|
||||
|
@ -1008,14 +1003,12 @@ class EditorDecorationContainerTest {
|
|||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_header_three_extra_space_top)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = view2.marginTop
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_header_three_extra_space_bottom)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = view2.marginBottom
|
||||
)
|
||||
}
|
||||
|
@ -1097,14 +1090,12 @@ class EditorDecorationContainerTest {
|
|||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_header_one_extra_space_top)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = view2.marginTop
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = context.resources.getDimension(R.dimen.default_header_one_extra_space_bottom)
|
||||
.toInt(),
|
||||
expected = 0,
|
||||
actual = view2.marginBottom
|
||||
)
|
||||
}
|
||||
|
|
|
@ -48,9 +48,9 @@ import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_MEDIA_CODE
|
|||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import timber.log.Timber
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import timber.log.Timber
|
||||
|
||||
fun Context.dimen(res: Int): Float {
|
||||
return resources
|
||||
|
|
|
@ -64,8 +64,22 @@ inline fun <T> List<T>.replace(replacement: (T) -> T, target: (T) -> Boolean): L
|
|||
return map { if (target(it)) replacement(it) else it }
|
||||
}
|
||||
|
||||
fun Context.toast(msg: CharSequence) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
|
||||
fun Fragment.toast(msg: CharSequence) = requireActivity().toast(msg)
|
||||
fun Context.toast(
|
||||
msg: CharSequence,
|
||||
duration: Int = Toast.LENGTH_SHORT
|
||||
) = Toast.makeText(
|
||||
this,
|
||||
msg,
|
||||
duration
|
||||
).show()
|
||||
|
||||
fun Fragment.toast(
|
||||
msg: CharSequence,
|
||||
duration: Int = Toast.LENGTH_SHORT
|
||||
) = requireActivity().toast(
|
||||
msg = msg,
|
||||
duration = duration
|
||||
)
|
||||
|
||||
fun Fragment.toast(@StringRes msgId: Int) = requireActivity().toast(requireActivity().getString(msgId))
|
||||
|
||||
|
@ -114,4 +128,12 @@ fun <T> MutableList<T>.moveAfterIndexInLine(
|
|||
} else {
|
||||
addAll(size, split.first)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> MutableList<T>.moveOnTop(
|
||||
predicate: (T) -> Boolean
|
||||
) {
|
||||
val (first, last) = partition(predicate)
|
||||
clear()
|
||||
addAll(first + last)
|
||||
}
|
|
@ -130,4 +130,23 @@ fun EditText.showKeyboard() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T>RecyclerView.isLastOfType() : Boolean {
|
||||
val count = itemDecorationCount
|
||||
return if (count > 0) {
|
||||
val lastIndex = count - 1
|
||||
val lastDecorator = getItemDecorationAt(lastIndex)
|
||||
lastDecorator is T
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun RecyclerView.lastDecorator() : RecyclerView.ItemDecoration? {
|
||||
val count = itemDecorationCount
|
||||
return if (count > 0)
|
||||
getItemDecorationAt(count - 1)
|
||||
else
|
||||
null
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.anytypeio.anytype.core_utils.tools
|
||||
|
||||
import android.os.Looper
|
||||
import javax.inject.Inject
|
||||
|
||||
interface ThreadInfo {
|
||||
fun isOnMainThread(): Boolean
|
||||
}
|
||||
|
||||
class DefaultThreadInfo @Inject constructor() : ThreadInfo {
|
||||
override fun isOnMainThread(): Boolean {
|
||||
return Looper.myLooper() == Looper.getMainLooper()
|
||||
}
|
||||
}
|