This repository has been archived by the owner on May 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #130 from material-foundation/develop
Move new documentation to stable
- Loading branch information
Showing
11 changed files
with
724 additions
and
230 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
 | ||
|
||
# Display the Remixer Fragment | ||
|
||
You can configure the `RemixerFragment` in the `Activity`'s `onCreate(Bundle)` method, after the call to `RemixerBinder.bind(this)`. You have 3 (not mutually-exclusive) options. You can see examples of how to do it below. | ||
|
||
*Note, however, that configuring the UI is optional if you want to exclusively use the Firebase Remote Controller functionality.* | ||
|
||
## Attach the Remixer Fragment to a Button | ||
You need to call `RemixerFragment#attachToButton(FragmentActivity, Button)` | ||
|
||
Your `Activity.onCreate` may look like this: | ||
|
||
```java | ||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
//... | ||
remixerButton = (Button) findViewById(R.id.button); | ||
RemixerBinder.bind(this); | ||
RemixerFragment.newInstance().attachToButton(this, remixerButton); | ||
} | ||
``` | ||
|
||
## Attach the Remixer Fragment to a multi-touch gesture | ||
You need to call `RemixerFragment#attachToGesture(FragmentActivity, Direction, int)` | ||
|
||
Your `Activity.onCreate` may look like this: | ||
|
||
```java | ||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
//... | ||
RemixerBinder.bind(this); | ||
RemixerFragment.newInstance().attachToGesture( | ||
this, | ||
Direction.UP, | ||
3 /* numberOfFingers */); | ||
} | ||
``` | ||
|
||
## Attach the Remixer Fragment to a shake | ||
You need to call `RemixerFragment#attachToShake(FragmentActivity, double)` from `onResume()` and call `RemixerFragment#detachFromShake()` from `onPause()` | ||
|
||
Your `Activity.onCreate` may look like this: | ||
|
||
```java | ||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
//... | ||
RemixerBinder.bind(this); | ||
remixerFragment = RemixerFragment.newInstance(); | ||
} | ||
|
||
@Override | ||
protected void onResume() { | ||
super.onResume(); | ||
remixerFragment.attachToShake(this, 20.0); | ||
} | ||
|
||
@Override | ||
protected void onPause() { | ||
super.onPause(); | ||
remixerFragment.detachFromShake(); | ||
} | ||
``` | ||
|
||
## Or combine all of them | ||
|
||
```java | ||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
// ... | ||
remixerButton = (Button) findViewById(R.id.button); | ||
RemixerBinder.bind(this); | ||
|
||
remixerFragment = RemixerFragment.newInstance(); | ||
remixerFragment.attachToGesture(this, Direction.UP, 3); | ||
remixerFragment.attachToButton(this, remixerButton); | ||
} | ||
|
||
@Override | ||
protected void onResume() { | ||
super.onResume(); | ||
remixerFragment.attachToShake(this, 20.0); | ||
} | ||
|
||
@Override | ||
protected void onPause() { | ||
super.onPause(); | ||
remixerFragment.detachFromShake(); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Extending Remixer | ||
|
||
There are several ways in which you can extend remixer: | ||
|
||
- Add a new `DataType`: | ||
- Create a `ValueConverter` for the `DataType` | ||
- Set as default layoutIDs by calling `DataType.setLayoutIdForVariableType(Class<? extends Variable>, int)` | ||
- Register it at `Application.onCreate()` time | ||
- can be done in `RemixerInitialization.initRemixer()` if you're adding it at the Remixer level (forking/contributing) | ||
- **Note**: At the time only forks/contributions can add annotation processing support for new DataTypes. If it proves necessary we'll write extension points for the annotation processor. | ||
- Implement a new `RemixerWidget` | ||
- Optionally, set it as a default Layout Id for a `DataType`/`Variable class` combination by calling `DataType.setLayoutIdForVariableType`. You can do it in `RemixerInitialization.initRemixer()` if forking/contributing. | ||
|
||
**Notice that if you plan to use the Firebase Remote Controller functionality, you need to replicate this work in the material-remixer-js and material-remixer-remote-web projects as well**. It is out of scope for this document to explain how to do it in those projects. | ||
|
||
## Adding a new DataType | ||
|
||
In order to add a new data type to Remixer, you need to construct a static instance of the DataType class... | ||
|
||
```java | ||
public static final DataType<RT, ST> MY_NEW_TYPE = new DataType<>( | ||
"myNewType", | ||
RT.class, | ||
ST.class, | ||
new MyNewTypeValueConverter("myNewType")); | ||
``` | ||
|
||
... and register it as a RemixerType somewhere that only gets called once (preferably during your `Application.onCreate()`)... | ||
|
||
```java | ||
public class MyApplication extends Application { | ||
|
||
@Override | ||
public void onCreate() { | ||
super.onCreate(); | ||
Remixer.registerDataType(MY_NEW_TYPE); | ||
} | ||
} | ||
``` | ||
|
||
Now let's dissect the first statement, the `new DataType<>(...)`: | ||
|
||
1. It has two generic parameters, RT and ST, the RuntimeType and the SerializedType. The former is used for callbacks during runtime, and the latter is used to serialize, store data and (potentially) sync it to Firebase. They can be exactly the same type, and they usually are. | ||
2. It has a unique identifier string, in the example it's "myNewType". It can be whatever you want but current identifier strings are "boolean", "color", "number" and "string". | ||
3. You need to write a subclass of `ValueConverter` which converts between the RuntimeType and the SerializedType and performs other serialization-related tasks. | ||
|
||
|
||
Now you can use it with any of the variable classes using the appropriate Builder. Just make sure to call `setDataType(MY_DATA_TYPE)` and you're done. | ||
|
||
## Add a new Variable subclass | ||
|
||
Variable subclasses represent constraints on what values the variable can take. | ||
|
||
1. Create a subclass of Variable. | ||
2. Override its `checkValue(T)` method. | ||
3. Override its `getSerializableConstraints()` method with a new constant constraint string that is unique to this class. | ||
4. Add new fields to `StoredVariable` representing new data required to represent the new variable subclass. | ||
5. Make sure you update `ValueConverter`'s `fromVariable(Variable<?>)`, `serialize(StoredVariable<SerializableType>)` and `deserialize(JsonElement)` methods to match the new fields. | ||
|
||
## Adding new annotation support | ||
|
||
If you add a new type or variable you may want to make it easier to use by adding annotation support. This is easy if you are forking or contributing to the android remixer repository, but otherwise we do not currently offer a way to extend the annnotation processor. We may if this becomes a problem down the line. | ||
|
||
1. Subclass `MethodAnnotation` to handle your new annotation classes. | ||
2. Add a new enum item per new annotation you're supporting to `SupportedMethodAnnotation` | ||
3. Add lots of tests, these errors are hard to debug, so please do your best to keep coverage high. | ||
|
||
## Implement a new RemixerWidget | ||
|
||
**Note:** Remixer relies on inflating widgets by layout id, so these cannot be pure-java, they need to have a layout resource associated with them. | ||
|
||
1. Write a new class that extends `android.view.ViewGroup` (usually either `LinearLayout` or `RelativeLayout`) and implements `RemixerWidget` | ||
2. Add a new layout resource whose element is of the class you just created. | ||
3. Optionally, set it as a default Layout Id for a `DataType`/`Variable class` combination by calling `DataType.setLayoutIdForVariableType(Class<? extends Variable>, int)`. You can do it in `RemixerInitialization.initRemixer()` if forking/contributing. | ||
|
||
Even if you do not set it as a default layout anywhere, you can still force to use it by setting the layoutId property on a variable. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
 | ||
|
||
# Getting started | ||
|
||
__Disclaimer:__ Remixer still hasn't reached a stage that we consider is stable enough to commit to the current status of the API, it will be evolving quickly and we may commit breaking changes every once in a while. _That being said_, we would love to have you try it out and tell us what you think is missing and what you'd like us to focus on. | ||
|
||
You can read our [javadoc for the current release (0.6.6)](https://jitpack.io/com/github/material-foundation/material-remixer-android/remixer/0.6.6/javadoc/index.html) and the [javadoc for HEAD](https://jitpack.io/com/github/material-foundation/material-remixer-android/remixer/develop-SNAPSHOT/javadoc/index.html) generated by jitpack. | ||
|
||
## Set up dependencies | ||
|
||
Using gradle it's super easy to start using Remixer following these instructions. | ||
|
||
In your main build.gradle file make sure you have the following dependencies and repositories set up: | ||
|
||
```gradle | ||
buildscript { | ||
dependencies { | ||
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' | ||
} | ||
} | ||
allprojects { | ||
repositories { | ||
jcenter() | ||
maven { url "https://jitpack.io" } | ||
} | ||
} | ||
``` | ||
|
||
And in your modules, apply the `android-apt` plugin and add the remixer dependencies: | ||
```gradle | ||
apply plugin: 'android-apt' | ||
dependencies { | ||
compile 'com.github.material-foundation.material-remixer-android:remixer:0.6.6' | ||
provided 'com.github.material-foundation.material-remixer-android:remixer_annotation:0.6.6' | ||
} | ||
``` | ||
|
||
Notice the dependency on `remixer_annotation` is a `provided` clause instead of `compile`, this is on purpose as this is not a regular dependency but a compiler plugin. | ||
|
||
Once you start depending on Remixer, your build will fail if no `google-services.json` exists on your app module. This is because Remixer has the option of using firebase to store and sync values, and whether you use it or not the build system looks for that file. It can be an empty file or the one we provide in `remixer_example/src/main/google-services.json`. | ||
|
||
## Initialize remixer | ||
|
||
1. Subclass the `android.app.Application`. | ||
2. [Declare it in your Android Manifest](https://developer.android.com/guide/topics/manifest/application-element.html#nm). | ||
3. Call the `RemixerInitialization#initRemixer(Application)` method. | ||
4. Set a synchronization mechanism for Remixer (you have a few options): | ||
- `com.google.android.libraries.remixer.sync.LocalValueSyncing`, this is the default (you don't need to set it if this is what you want), it doesn't persist any values but makes sure values sync across different contexts (Activities). | ||
- `com.google.android.libraries.remixer.storage.LocalStorage`, this stores values locally in a SharedPreferences file. | ||
- `com.google.android.libraries.remixer.storage.FirebaseRemoteControllerSyncer`, this syncs values to and from a firebase instance to use it as a remote controller. **This functionality is not complete yet** | ||
|
||
For example: | ||
|
||
```java | ||
import android.app.Application; | ||
import com.google.android.libraries.remixer.Remixer; | ||
import com.google.android.libraries.remixer.storage.LocalStorage; | ||
import com.google.android.libraries.remixer.ui.RemixerInitialization; | ||
|
||
class MyApplication extends Application { | ||
@Override | ||
public void onCreate() { | ||
super.onCreate(); | ||
RemixerInitialization.initRemixer(this); | ||
Remixer.getInstance().setSynchronizationMechanism(new LocalStorage(getApplicationContext())); | ||
} | ||
} | ||
``` | ||
|
||
## Define Variables | ||
|
||
You can define variables in an activty by writing methods that take one argument of the correct type and annotate them. The methods contain your logic to handle changes to these variables (update the UI accordingly). | ||
|
||
You can rest assured those methods will run in the main UI thread. | ||
|
||
There are a few very simple examples here, but you should look at the [example](https://github.com/material-foundation/material-remixer-android/blob/develop/remixer_example/src/main/java/com/google/android/apps/remixer/MainActivity.java) [activities](https://github.com/material-foundation/material-remixer-android/blob/develop/remixer_example/src/main/java/com/google/android/apps/remixer/BoxActivity.java) and [documentation for these annotations](https://github.com/material-foundation/material-remixer-android/tree/develop/remixer_core/src/main/java/com/google/android/libraries/remixer/annotation) for more information. | ||
|
||
For example: | ||
|
||
```java | ||
@RangeVariableMethod(minValue = 15, maxValue = 70, initialValue = 20) | ||
public void setFontSize(Float fontSize) { | ||
titleText.setTextSize(TypedValue.COMPLEX_UNIT_SP, size); | ||
} | ||
``` | ||
|
||
Additionally, you need to add `RemixerBinder.bind(this)` at the end of your activity's `onCreate(Bundle)`. | ||
|
||
```java | ||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
//... | ||
RemixerBinder.bind(this); | ||
} | ||
``` | ||
|
||
## Display the Remixer Fragment | ||
|
||
You can configure the `RemixerFragment` in the `Activity`'s `onCreate(Bundle)` method, after the call to `RemixerBinder.bind(this)`. You have 3 (not mutually-exclusive) options: | ||
|
||
1. [Attach the RemixerFragment to a button click](CONFIGURE_UI.md#attach-the-remixer-fragment-to-a-button), `RemixerFragment#attachToButton(FragmentActivity, Button)` | ||
2. [Attach the RemixerFragment to a multi-touch gesture](CONFIGURE_UI.md#attach-the-remixer-fragment-to-a-multi-touch-gesture), `RemixerFragment#attachToButton(FragmentActivity, Button)` | ||
3. [Attach the RemixerFragment to a ShakeListener](CONFIGURE_UI.md#attach-the-remixer-fragment-to-a-shake), `RemixerFragment#attachToShake(FragmentActivity, double)`. *In order to conserve battery, you must call `attachToShake` from `onResume` and call `detachFromShake` from `onPause`* | ||
|
||
Detailed examples can be found on the [Configure the UI](CONFIGURE_UI.md) page. | ||
|
||
|
||
_You may not want to use the `RemixerFragment` in case you only want to use a FirebaseRemoteController. Adding a RemixerFragment does affect the UI you may be playing with, so it may be desirable not to use it and tweak remotely instead._ |
Oops, something went wrong.