Skip to content
This repository has been archived by the owner on May 15, 2023. It is now read-only.

Add second activity to example app #37

Merged
merged 5 commits into from
Oct 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 37 additions & 19 deletions remixer_example/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,44 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.apps.remixer"
tools:ignore="GoogleAppIndexingWarning">

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.apps.remixer">
<application
android:name=".RemixerApplication"
android:allowBackup="false"
android:icon="@drawable/logo_remixer_color_48"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:allowBackup">
<activity
android:name=".MenuActivity"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<application
android:icon="@drawable/logo_remixer_color_48"
android:label="@string/app_name"
android:supportsRtl="true"
android:name=".RemixerApplication"
android:theme="@style/AppTheme">
<activity
android:name="com.google.android.apps.remixer.MainActivityAnnotated"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:parentActivityName=".MenuActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MenuActivity" />
</activity>
<activity
android:name=".BoxActivity"
android:parentActivityName=".MenuActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MenuActivity" />
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.google.android.apps.remixer;

import android.graphics.Color;
import android.media.Image;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.libraries.remixer.BooleanVariableBuilder;
import com.google.android.libraries.remixer.Callback;
import com.google.android.libraries.remixer.ItemListVariable;
import com.google.android.libraries.remixer.RangeVariable;
import com.google.android.libraries.remixer.Variable;
import com.google.android.libraries.remixer.Remixer;
import com.google.android.libraries.remixer.StringVariableBuilder;
import com.google.android.libraries.remixer.Trigger;
import com.google.android.libraries.remixer.annotation.BooleanVariableMethod;
import com.google.android.libraries.remixer.annotation.IntegerListVariableMethod;
import com.google.android.libraries.remixer.annotation.RangeVariableMethod;
import com.google.android.libraries.remixer.annotation.RemixerBinder;
import com.google.android.libraries.remixer.ui.gesture.Direction;
import com.google.android.libraries.remixer.ui.view.RemixerFragment;

/**
* This activity reuses one of the values from variables in MainActivity.
*/
public class BoxActivity extends AppCompatActivity {

// A title text whose text size is set by a variable.
private TextView titleText;
// An ImageView that does nothing but draw a box with its background color.
private ImageView box;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting and blank lines for these fields

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_box);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Find all UI widgets
titleText = (TextView) findViewById(R.id.titleText);
box = (ImageView) findViewById(R.id.box);
// Initialize the remixer instance
RemixerBinder.bind(this);
RemixerFragment remixerFragment = RemixerFragment.newInstance();
remixerFragment.attachToGesture(this, Direction.UP, 3);
}

@RangeVariableMethod(
key = "titleTextSize", title = "(Shared) title text size",
minValue = 16, maxValue = 72, increment = 4
)
public void setTitleTextSize(Integer size) {
titleText.setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}

@BooleanVariableMethod(key = "showBox", title = "Show box")
void showBox(Boolean show) {
box.setVisibility(show ? View.VISIBLE : View.GONE);
}

@IntegerListVariableMethod(
key = "boxColor", title = "Box color",
possibleValues = {Color.DKGRAY, Color.LTGRAY, Color.MAGENTA, Color.CYAN}
)
void setBoxColor(Integer color) {
box.setBackgroundColor(color);
}
}

Original file line number Diff line number Diff line change
@@ -1,129 +1,79 @@
/*
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.apps.remixer;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.libraries.remixer.BooleanVariableBuilder;
import com.google.android.libraries.remixer.Callback;
import com.google.android.libraries.remixer.ItemListVariable;
import com.google.android.libraries.remixer.RangeVariable;
import com.google.android.libraries.remixer.Variable;
import com.google.android.libraries.remixer.Remixer;
import com.google.android.libraries.remixer.StringVariableBuilder;
import com.google.android.libraries.remixer.Trigger;
import com.google.android.libraries.remixer.annotation.RangeVariableMethod;
import com.google.android.libraries.remixer.annotation.RemixerBinder;
import com.google.android.libraries.remixer.annotation.StringListVariableMethod;
import com.google.android.libraries.remixer.annotation.StringVariableMethod;
import com.google.android.libraries.remixer.annotation.TriggerMethod;
import com.google.android.libraries.remixer.ui.gesture.Direction;
import com.google.android.libraries.remixer.ui.view.RemixerFragment;

/**
* Main activity with explicit instantiation of Remixer Objects.
* Example activity where remixer is used to control text size and text values.
*/
public class MainActivity extends AppCompatActivity {

// A text view whose text is updated by an ItemListVariable<String> and font size by a
// RangeVariable
private TextView boundedText;
// A text view whose text is updated by a StringVariable and is visible depending on a
// BooleanVariable
private TextView titleText;
private TextView freeformText;
// The remixer instance
private Remixer remixer;
private Button remixerButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Find all UI widgets
Button remixerButton = (Button) findViewById(R.id.button);
boundedText = (TextView) findViewById(R.id.boundedText);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
titleText = (TextView) findViewById(R.id.titleText);
freeformText = (TextView) findViewById(R.id.freeformText);
// Initialize the remixer instance
remixer = Remixer.getInstance();

ItemListVariable.Builder<Integer> colorVariable = new ItemListVariable.Builder<Integer>()
.setKey("color")
.setParentObject(this)
.setPossibleValues(new Integer[] {Color.DKGRAY, Color.LTGRAY, Color.MAGENTA, Color.CYAN} )
.setCallback(new Callback<Integer>() {
@Override
public void onValueSet(Variable<Integer> variable) {
boundedText.setTextColor(variable.getSelectedValue());
freeformText.setTextColor(variable.getSelectedValue());
}
})
.setLayoutId(R.layout.color_list_variable_widget);
remixer.addItem(colorVariable.buildAndInit());
remixerButton = (Button) findViewById(R.id.button);
RemixerBinder.bind(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not for this PR, but: could this call be replaced with an @annotation on the Activity?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It generally can't, this is because of the way Annotation processing works, all such frameworks use a similar method to tie things together :(


// Create a RangeVariable that updates boundedText's size between 10 and 48 sp.
RangeVariable.Builder fontSizeRangeVariable = new RangeVariable.Builder()
.setKey("font_size")
.setParentObject(this)
.setMinValue(16)
.setMaxValue(72)
.setIncrement(4)
.setCallback(new Callback<Integer>() {
@Override
public void onValueSet(Variable<Integer> variable) {
boundedText.setTextSize(TypedValue.COMPLEX_UNIT_SP, variable.getSelectedValue());
}
});
remixer.addItem(fontSizeRangeVariable.buildAndInit());

// Create an ItemListVariable<String> that updates boundedText's contents from a list of options
ItemListVariable.Builder<String> itemListVariable = new ItemListVariable.Builder<String>()
.setKey("boundedText")
.setParentObject(this)
.setPossibleValues(new String[] {"Hello world", "Foo", "Bar", "May the force be with you"})
.setCallback(
new Callback<String>() {
@Override
public void onValueSet(Variable<String> variable) {
boundedText.setText(variable.getSelectedValue());
}
});
remixer.addItem(itemListVariable.buildAndInit());
RemixerFragment remixerFragment = RemixerFragment.newInstance();
remixerFragment.attachToGesture(this, Direction.UP, 3);
remixerFragment.attachToButton(this, remixerButton);
}

// Create a BooleanVariable that controls whether freeformText is visible or not.
Variable.Builder<Boolean> booleanVariable = new BooleanVariableBuilder()
.setKey("freeformTextDisplay")
.setParentObject(this)
.setCallback(new Callback<Boolean>() {
@Override
public void onValueSet(Variable<Boolean> variable) {
freeformText.setVisibility(variable.getSelectedValue() ? View.VISIBLE : View.GONE);
}
});
remixer.addItem(booleanVariable.buildAndInit());
@RangeVariableMethod(
key = "titleTextSize",
minValue = 16, maxValue = 72, increment = 4, title = "(Shared) Title font size")
void setTitleTextSize(Integer size) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I'm reading this right, then the key of an annotated method is the simple method name? And this is how you ensure this Variable is shared across 2 activities?

This is really clever, but it's a really difficult to discover API. This is also prone to errors: imagine if a client has two different Activities, each with an annotated setTextSize() (maybe they're lazy) but meant to target completely different widgets. All of a sudden, these 2 variables are linked due to having the same key/name, and it's probably a huge pain trying to debug why it's happening (without your help).

I would suggest changing it, perhaps to something more explicit. I have some ideas, but can you first enumerate the reasons why we have keys? I'll start us off:

  1. Two RemixerItems can have the same key in order to share the current value. The constraint is that they must both share the same Type and their parent Activity must differ.
  2. A RemixerItem's key is used to connect it to the corresponding remixer Widget so updates in the widget are correctly routed to the right callback. (is this true?) The constraint is that all RemixerItems under the same parent Activity must have different keys.

Anything else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key can be set explicitly, remember a while ago we were trying to minimize the amount of boilerplate a user needed to write, key was one of them, so it takes a sensible default.

I can still change it to something more explicit, and maybe remove auto key generation if you think it's that problematic.

Also you're correct.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should definitely keep an eye out for whether this trips anyone up. And/or think about when an unintended key collision happens, how you can help the client debug. All of this can be done after this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious if this param can be replaced with int? It may be better as an example for this to be int as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it can't :/ Java is very dumb about unboxing templated stuff. :/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or rather, I'm checking that the parameter is of a specific class, and since templates don't work with primitives and primitives aren't classes, it's mandatory to use the boxed version, I will investigate whether there is a way to compare to the unboxed type, but I don't think that's likely.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whaaat really? I think this deserves another look (later). There must be a way to support this.

I think this is pretty important, or devs who add @RemixerAnnotations to existing code will find it to be a major PITA.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #40 to investigate

titleText.setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}

// Create a StringVariable that lets you set freeformText's content freely.
Variable.Builder<String> freeformStringVariable = new StringVariableBuilder()
.setKey("freeformText")
.setParentObject(this)
.setDefaultValue("Change me!")
.setCallback(new Callback<String>() {
@Override
public void onValueSet(Variable<String> variable) {
freeformText.setText(variable.getSelectedValue());
}
});
remixer.addItem(freeformStringVariable.buildAndInit());
@StringListVariableMethod(
title = "Title text",
possibleValues = {"Hello World", "Alohomora", "Foo", "Bar", "May the force be with you"})
void setTitleText(String text) {
titleText.setText(text);
}

Trigger trigger = new Trigger("Toast", "toast", this, new Runnable() {
@Override
public void run() {
String value = freeformText.getText().toString();
Toast.makeText(MainActivity.this, value, Toast.LENGTH_SHORT).show();
}
});
remixer.addItem(trigger);
@StringVariableMethod(defaultValue = "Change me!")
void setFreeformText(String text) {
freeformText.setText(text);
}

RemixerFragment remixerFragment = RemixerFragment.newInstance();
remixerFragment.attachToButton(this, remixerButton);
remixerFragment.attachToGesture(this, Direction.UP, 3);
@TriggerMethod
void toast() {
Toast.makeText(this, freeformText.getText(), Toast.LENGTH_SHORT).show();
}
}

Loading