1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-09 12:32:30 +01:00

Add docs for compose

This commit is contained in:
Allan Wang 2023-06-27 14:14:46 -07:00
parent f5b003298e
commit 7c055efbf2
3 changed files with 90 additions and 2 deletions

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DesignSurface"> <component name="DesignSurface">
<option name="filePathToZoomLevelMap"> <option name="filePathToZoomLevelMap">
@ -7,12 +8,13 @@
</map> </map>
</option> </option>
</component> </component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="NullableNotNullManager"> <component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" /> <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" /> <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables"> <option name="myNullables">
<value> <value>
<list size="14"> <list size="18">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" /> <item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
@ -27,12 +29,16 @@
<item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" /> <item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" /> <item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" /> <item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" />
<item index="14" class="java.lang.String" itemvalue="org.jspecify.nullness.Nullable" />
<item index="15" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="16" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
<item index="17" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
</list> </list>
</value> </value>
</option> </option>
<option name="myNotNulls"> <option name="myNotNulls">
<value> <value>
<list size="14"> <list size="18">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
@ -47,6 +53,10 @@
<item index="11" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" /> <item index="11" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
<item index="12" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" /> <item index="12" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
<item index="13" class="java.lang.String" itemvalue="lombok.NonNull" /> <item index="13" class="java.lang.String" itemvalue="lombok.NonNull" />
<item index="14" class="java.lang.String" itemvalue="org.jspecify.nullness.NonNull" />
<item index="15" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="16" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
<item index="17" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
</list> </list>
</value> </value>
</option> </option>

View File

@ -1,5 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="IssueNavigationConfiguration">
<option name="links">
<list>
<IssueNavigationLink>
<option name="issueRegexp" value="\bb/(\d+)(#\w+)?\b" />
<option name="linkRegexp" value="https://buganizer.corp.google.com/issues/$1$2" />
</IssueNavigationLink>
<IssueNavigationLink>
<option name="issueRegexp" value="\b(?:BUG=|FIXED=)(\d+)\b" />
<option name="linkRegexp" value="https://buganizer.corp.google.com/issues/$1" />
</IssueNavigationLink>
<IssueNavigationLink>
<option name="issueRegexp" value="\b(?:cl/|cr/|OCL=|DIFFBASE=|ROLLBACK_OF=)(\d+)\b" />
<option name="linkRegexp" value="https://critique.corp.google.com/$1" />
</IssueNavigationLink>
<IssueNavigationLink>
<option name="issueRegexp" value="\bomg/(\d+)\b" />
<option name="linkRegexp" value="https://omg.corp.google.com/$1" />
</IssueNavigationLink>
<IssueNavigationLink>
<option name="issueRegexp" value="\b(?:go/|goto/)([^,.&lt;&gt;()&quot;\s]+(?:[.,][^,.&lt;&gt;()&quot;\s]+)*)" />
<option name="linkRegexp" value="https://goto.google.com/$1" />
</IssueNavigationLink>
<IssueNavigationLink>
<option name="issueRegexp" value="\bcs/([^\s]+[\w$])" />
<option name="linkRegexp" value="https://cs.corp.google.com/search/?q=$1" />
</IssueNavigationLink>
<IssueNavigationLink>
<option name="issueRegexp" value="(LINT\.IfChange)|(LINT\.ThenChange)" />
<option name="linkRegexp" value="https://goto.google.com/ifthisthenthatlint" />
</IssueNavigationLink>
</list>
</option>
</component>
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
</component> </component>

44
docs/Compose.md Normal file
View File

@ -0,0 +1,44 @@
# Jetpack Compose
Below are some findings and demos for jetpack compose during my explorations in this project. As with most implementation, I believe that anything is possible. This holds true with view implementations of the design below, though we will see that compose makes some elements considerably easier.
All links to code snippets are permalinks, but feel free to view changes at HEAD in case there are updates.
## Settings
> [Settings code](https://github.com/AllanWang/Frost-for-Facebook/tree/f5b003298ee91056e86a63c1f50c25285af45c9b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings)
Compose does not have any settings library, but it is actually very easy to do with [material `ListItem`s](https://m3.material.io/components/lists). This would also be easy with views + recyclerviews, though it seems like it is not being planned for MDC. With list items, we simply change the trailing content to provide what we need, be it a switch, checkbox, text, color selector, etc.
<todo add demo>
For most older apps, the standard is to use android preference xmls to build layouts. There are some downsides:
1. They are primarily built to use shared preferences, which we may not use
2. Custom views require some more wrapping to integrate with preferences
3. We don't control the base layouts at all (though we can modify some via themes)
4. Material no longer supports them
By converting to our own full implementation like in the snippet above, we have full control over the layouts, can add custom ones, and have all the benefits of compose to build relations between preferences, or between pages through the nav graph.
## Animations
### Stateless Animations
Compose makes it easy to animate from one state value to another. For instance, if scale is 1.0 by default, but should be 1.5 when pressed, we simply use `animateFloatAsState` and provide the expected value. However, there are cases where we may want to start and stop at the same value. This is pretty straightforward with views (`View.animate()`, `ValueAnimator`, etc), though compose supports this too:
We can look at the [overview graph](https://developer.android.com/jetpack/compose/animation/introduction#overview) and see some more basic building blocks, including `Animation` and `Animatable`. Both of these allow for initial values + velocity. Even if the initial and target values are the same, we can include velocity (or keyframes) to keep an animation going.
You can see an example through our [shake](https://github.com/AllanWang/Frost-for-Facebook/blob/f5b003298ee91056e86a63c1f50c25285af45c9b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/effects/Shake.kt) effect. `shake()` is called on click, and shakes that happen before the animation ends will smoothly restart the effect, but from the current rotation value using spring animations.
### Drag & Drop
Dragging between two visual elements is done similarly with views and compose:
* Replicate the element being dragged, and optionally hiding the original layout
* Ensure that the drag element can be drawn across the entire draggable region
* Provide reactions to the drag element when it hovers over other elements
* Make space for the dropped element and build the new layout after the transition is complete
With compose, it is extremely easy to listen to global coordinates, and to add/alter visual elements immediately in the next frame. We simply have listeners for all offsets, and a composable that optionally draws content. The same is possible with views, but takes a bit more coordination to actually add and remove the views. For dragging specifically, compose provides helpers including `detectDragGesturesAfterLongPress`, which does most of the work needed. No need for a `SimpleOnGestureListener`.
![Demo](https://user-images.githubusercontent.com/6251823/247897796-83a1ed67-d21a-4a4a-bef6-98c171fca655.mp4)