diff --git a/app/build.gradle b/app/build.gradle index a0c06c6..2b36161 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -80,7 +80,10 @@ dependencies { androidTestCompile 'com.android.support:support-annotations:23.4.0' // mockito - androidTestCompile 'org.mockito:mockito-core:1.9.5' + androidTestCompile 'org.mockito:mockito-core:1.10.19' + androidTestCompile "com.crittercism.dexmaker:dexmaker:1.4" + androidTestCompile "com.crittercism.dexmaker:dexmaker-dx:1.4" + androidTestCompile "com.crittercism.dexmaker:dexmaker-mockito:1.4" // our library compile 'com.pileproject:drivecommand:2.1.0' diff --git a/app/src/androidTest/java/com/pileproject/drive/programming/visual/block/NumberTextViewDelegateTest.java b/app/src/androidTest/java/com/pileproject/drive/programming/visual/block/NumberTextViewDelegateTest.java new file mode 100644 index 0000000..4332c5f --- /dev/null +++ b/app/src/androidTest/java/com/pileproject/drive/programming/visual/block/NumberTextViewDelegateTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block; + +import android.support.test.runner.AndroidJUnit4; +import android.widget.TextView; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import java.math.BigDecimal; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +@RunWith(AndroidJUnit4.class) +public class NumberTextViewDelegateTest { + + private static final int PRECISION = 3; + + public TextView mockTextView; + + public NumberTextHolder mockNumberTextHolder; + + public NumberTextViewDelegate numberTextViewDelegate; + + @Before + public void before() { + mockTextView = Mockito.mock(TextView.class); + + mockNumberTextHolder = Mockito.mock(NumberTextHolder.class); + + doReturn(PRECISION).when(mockNumberTextHolder).getPrecision(); + + numberTextViewDelegate = new NumberTextViewDelegate(mockTextView, mockNumberTextHolder); + } + + @Test + public void whenTextViewReturnsNumber_thenGetValueSucceeds() throws Exception { + + doReturn("2.500").when(mockTextView).getText(); + assertEquals(new BigDecimal("2.5"), numberTextViewDelegate.getValue()); + } + + @Test + public void whenTextViewReturnsNumber_thenGetActionValueSucceeds() throws Exception { + + doReturn("2.500").when(mockTextView).getText(); + assertEquals(2500, numberTextViewDelegate.getActionValue()); + } + + @Test + public void whenTextViewDisabled_thenVerifyTheMethodsCalled() throws Exception { + + numberTextViewDelegate.enableTextView(false); + + verify(mockTextView).setFocusable(false); + verify(mockTextView).setFocusableInTouchMode(false); + verify(mockTextView).setEnabled(false); + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/pileproject/drive/util/development/UnitTest.java b/app/src/androidTest/java/com/pileproject/drive/util/development/UnitTest.java new file mode 100644 index 0000000..6d5967e --- /dev/null +++ b/app/src/androidTest/java/com/pileproject/drive/util/development/UnitTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.util.development; + +import android.content.res.Configuration; +import android.content.res.Resources; + +import com.pileproject.drive.app.DriveApplication; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; + +import java.util.Locale; + +import static junit.framework.Assert.assertEquals; + +@RunWith(Enclosed.class) +public class UnitTest { + + public static void setLocale(Locale locale) { + + Resources res = DriveApplication.getContext().getResources(); + Configuration config = res.getConfiguration(); + config.locale = locale; + res.updateConfiguration(config, res.getDisplayMetrics()); + } + + public static class InUS { + + @Before + public void before() { + setLocale(Locale.US); + } + + @Test + public void whenUnitIsSecond_thenDecoratesAsSecondString() throws Exception { + assertEquals("When value is 1.0 and unit is second", + "1.000s", Unit.Second.decorateValue(Locale.ENGLISH, 1.0, 3)); + } + + @Test + public void whenUnitIsPercent_thenDecoratesAsPercentString() throws Exception { + assertEquals("When value is 100 and unit is percentage", + "100%", Unit.Percentage.decorateValue(Locale.ENGLISH, 100, 0)); + } + + @Test + public void whenUnitIsNumberOfTimes_thenDecoratesAsNumberOfTimesString() throws Exception { + assertEquals("When the value 100 and unit is NumberOfTimes", + "Repeats: 100", Unit.NumberOfTimes.decorateValue(Locale.ENGLISH, 100, 0)); + } + } + + public static class InJP { + + @Before + public void before() { + setLocale(Locale.JAPAN); + } + + @Test + public void whenUnitIsSecond_thenDecoratesAsSecondString() throws Exception { + assertEquals("When value is 1.0 and unit is second", + "1.000びょう", Unit.Second.decorateValue(Locale.JAPAN, 1.0, 3)); + } + + @Test + public void whenUnitIsPercent_thenDecoratesAsPercentString() throws Exception { + assertEquals("When value is 100 and unit is percentage", + "100%", Unit.Percentage.decorateValue(Locale.JAPAN, 100, 0)); + } + + @Test + public void whenUnitIsNumberOfTimes_thenDecoratesAsNumberOfTimesString() throws Exception { + assertEquals("When the value 100 and unit is NumberOfTimes", + "100 かい", Unit.NumberOfTimes.decorateValue(Locale.JAPAN, 100, 0)); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/database/ProgramDataManager.java b/app/src/main/java/com/pileproject/drive/database/ProgramDataManager.java index f93bbd7..d95fb65 100644 --- a/app/src/main/java/com/pileproject/drive/database/ProgramDataManager.java +++ b/app/src/main/java/com/pileproject/drive/database/ProgramDataManager.java @@ -22,7 +22,7 @@ import com.pileproject.drive.programming.visual.activity.BlockPositionComparator; import com.pileproject.drive.programming.visual.block.BlockBase; import com.pileproject.drive.programming.visual.block.BlockFactory; -import com.pileproject.drive.programming.visual.block.NumTextHolder; +import com.pileproject.drive.programming.visual.block.NumberTextHolder; import com.pileproject.drive.programming.visual.layout.BlockSpaceLayout; import com.yahoo.squidb.data.SquidCursor; import com.yahoo.squidb.sql.Query; @@ -129,8 +129,8 @@ private boolean saveProgram(String programName, String programType, BlockSpaceLa .setLeft(b.getLeft()) .setTop(b.getTop()); // get the number of TextView if the block has one - if (b instanceof NumTextHolder) { - data.setNumber(((NumTextHolder) b).getNum()); + if (b instanceof NumberTextHolder) { + data.setNumber(((NumberTextHolder) b).getValueAsString()); } // save the data @@ -202,8 +202,8 @@ private ArrayList loadBlocks(SquidCursor c) { // set data's properties b.setLeft(data.getLeft()); b.setTop(data.getTop()); - if (b instanceof NumTextHolder) { - ((NumTextHolder) b).setNum(data.getNumber()); + if (b instanceof NumberTextHolder) { + ((NumberTextHolder) b).setValueAsString(data.getNumber()); } // add the block to a list blocks.add(b); diff --git a/app/src/main/java/com/pileproject/drive/database/ProgramDataSpec.java b/app/src/main/java/com/pileproject/drive/database/ProgramDataSpec.java index 8c3dabc..b08f265 100644 --- a/app/src/main/java/com/pileproject/drive/database/ProgramDataSpec.java +++ b/app/src/main/java/com/pileproject/drive/database/ProgramDataSpec.java @@ -47,5 +47,5 @@ public class ProgramDataSpec { // the number which a number holder block has @ColumnSpec(defaultValue = "0") - int number; + String number; } \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/execution/ExecutionCondition.java b/app/src/main/java/com/pileproject/drive/execution/ExecutionCondition.java index 4a0fb8b..72c37c1 100644 --- a/app/src/main/java/com/pileproject/drive/execution/ExecutionCondition.java +++ b/app/src/main/java/com/pileproject/drive/execution/ExecutionCondition.java @@ -16,10 +16,7 @@ package com.pileproject.drive.execution; - import com.pileproject.drive.programming.visual.block.BlockBase; -import com.pileproject.drive.programming.visual.block.repetition.RepetitionEndBlock; -import com.pileproject.drive.programming.visual.block.repetition.WhileForeverBlock; import java.util.ArrayList; import java.util.Collections; @@ -31,6 +28,8 @@ * A container class that has the condition of program execution. */ public class ExecutionCondition { + private static final int FOREVER_WHILE_OFFSET = -1000; + private Stack mWhileStack; private Stack mIfStack; private int mBeginningOfCurrentLoop; @@ -147,10 +146,29 @@ public int sizeOfSelectionResult() { * Push the index of the beginning block of the current loop to a stack. * @param index the index of the beginning block */ - public void pushBeginningOfLoop(int index) { + private void pushBeginningOfLoop(int index) { mWhileStack.push(index); mBeginningOfCurrentLoop = index >= 0 ? - index : index - WhileForeverBlock.FOREVER_WHILE_OFFSET; + index : index - FOREVER_WHILE_OFFSET; + } + + /** + * Enters infinite loop. + */ + public void enterInfiniteLoop() { + int index = getProgramCount(); + pushBeginningOfLoop(index + FOREVER_WHILE_OFFSET); // push with offset + } + + /** + * Enters N-times loop. + * @param count number of times this loop repeats. + */ + public void enterNTimesLoop(int count) { + int index = getProgramCount(); + for (int i = 1; i < count; i++) { + pushBeginningOfLoop(index); + } } /** @@ -160,7 +178,7 @@ public void reachEndOfLoop() { if (mWhileStack.isEmpty()) return ; int index = mWhileStack.peek() >= 0 ? - mWhileStack.peek() : mWhileStack.peek() - WhileForeverBlock.FOREVER_WHILE_OFFSET; + mWhileStack.peek() : mWhileStack.peek() - FOREVER_WHILE_OFFSET; // the loop has already finished if (mBeginningOfCurrentLoop != index) { @@ -189,7 +207,7 @@ public void breakLoop() { // update index if (!mWhileStack.isEmpty()) { mBeginningOfCurrentLoop = mWhileStack.peek() >= 0 ? - mWhileStack.peek() : mWhileStack.peek() - WhileForeverBlock.FOREVER_WHILE_OFFSET; + mWhileStack.peek() : mWhileStack.peek() - FOREVER_WHILE_OFFSET; } else { mBeginningOfCurrentLoop = -1; } @@ -199,7 +217,7 @@ public void breakLoop() { // move to the end of the current loop for (; mBlocks.size() >= mProgramCount; ++mProgramCount) { - if (mBlocks.get(mProgramCount).getKind() == RepetitionEndBlock.class) break; + if (mBlocks.get(mProgramCount).getKind() == BlockBase.BlockKind.REPETITION_END) break; } } } diff --git a/app/src/main/java/com/pileproject/drive/execution/ExecutionThread.java b/app/src/main/java/com/pileproject/drive/execution/ExecutionThread.java index 1708729..6122f74 100644 --- a/app/src/main/java/com/pileproject/drive/execution/ExecutionThread.java +++ b/app/src/main/java/com/pileproject/drive/execution/ExecutionThread.java @@ -25,6 +25,7 @@ import com.pileproject.drive.app.DriveApplication; import com.pileproject.drive.database.ProgramDataManager; import com.pileproject.drive.programming.visual.block.BlockBase; +import com.pileproject.drive.programming.visual.block.selection.SelectionEndBlock; /** * A Thread class to execute program. diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/activity/ProgrammingActivityBase.java b/app/src/main/java/com/pileproject/drive/programming/visual/activity/ProgrammingActivityBase.java index 96a8c3f..3676f2c 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/activity/ProgrammingActivityBase.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/activity/ProgrammingActivityBase.java @@ -148,7 +148,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { // Get results int howToMake = data.getIntExtra(getString(R.string.key_block_how_to_make), BlockFactory.SEQUENCE); String blockName = data.getStringExtra(getString(R.string.key_block_block_name)); - ArrayList blocks = BlockFactory.createBlocks(howToMake, blockName); + List blocks = BlockFactory.createBlocks(howToMake, blockName); mSpaceManager.addBlocks(blocks); } break; diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/BlockBase.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/BlockBase.java index 392874d..32b5555 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/BlockBase.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/BlockBase.java @@ -17,6 +17,8 @@ package com.pileproject.drive.programming.visual.block; import android.content.Context; +import android.support.annotation.LayoutRes; +import android.view.LayoutInflater; import android.widget.RelativeLayout; import com.pileproject.drive.execution.ExecutionCondition; @@ -29,10 +31,20 @@ * @version 1.0 18-June-2013 */ public abstract class BlockBase extends RelativeLayout { - public BlockBase(Context context) { + + public BlockBase(Context context, @LayoutRes int layoutRes) { super(context); + + LayoutInflater.from(context).inflate(layoutRes, this); } + /** + * Returns kind of this block. + * + * @return {@link BlockKind} + */ + public abstract BlockKind getKind(); + /** * Action that this block does while the execution of program. * Return delay that occurs after this action @@ -45,10 +57,7 @@ public BlockBase(Context context) { public abstract int action( MachineController controller, ExecutionCondition condition); - /** - * Return class to check a program is correct or not - * - * @return Class - */ - public abstract Class getKind(); + public enum BlockKind { + SEQUENCE, SELECTION_BEGIN, SELECTION_END, REPETITION_BEGIN, REPETITION_END, REPETITION_BREAK + } } \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/BlockFactory.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/BlockFactory.java index a4167c9..77b1dbd 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/BlockFactory.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/BlockFactory.java @@ -17,16 +17,16 @@ package com.pileproject.drive.programming.visual.block; import android.content.Context; +import android.support.annotation.IntDef; import com.pileproject.drive.app.DriveApplication; import com.pileproject.drive.programming.visual.block.repetition.RepetitionEndBlock; import com.pileproject.drive.programming.visual.block.selection.SelectionEndBlock; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; - -import trikita.log.Log; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Factory that creates blocks @@ -38,131 +38,80 @@ public class BlockFactory { public static final int SEQUENCE = 0; public static final int REPETITION = 1; public static final int SELECTION = 2; - public static final int UNDO = 3; public static final int LOAD = 4; - /** - * This class can't be created as an instance - */ private BlockFactory() { } - /** - * Return class by name - * - * @param className - * @return Class - */ @SuppressWarnings("unchecked") - private static Class getClassForName(String className) throws RuntimeException { + private static Class getClassForName(String className) throws RuntimeException { try { return (Class) Class.forName(className); } catch (ClassNotFoundException e) { - throw new RuntimeException(e); + throw new RuntimeException("Invalid class name '" + className + "'", e); } } - /** - * Create blocks by reflecting - * - * @param blockName - * @return - */ - private static BlockBase create(String blockName) { - - // Get class - Class blockClass = null; - try { - blockClass = getClassForName(blockName); - } catch (RuntimeException e) { - Log.e(e.getMessage()); - } - - // Get constructor - Class[] types = {Context.class}; - Constructor constructor; + private static BlockBase create(Class blockClass) throws RuntimeException { try { - constructor = blockClass.getConstructor(types); - } catch (SecurityException | NoSuchMethodException e) { - throw new RuntimeException(e); + Constructor constructor = blockClass.getConstructor(Context.class); + return constructor.newInstance(DriveApplication.getContext()); + } catch (Exception e) { + // there's no way to recover. let App die. + throw new RuntimeException("Failed to instantiate " + blockClass, e); } + } - // Create a new instance - Object[] args = {DriveApplication.getContext()}; - BlockBase b; - try { - b = constructor.newInstance(args); - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - return b; + private static List createSequenceBlock(Class blockClass) { + return Collections.singletonList(create(blockClass)); } - /** - * Create a sequence block - * - * @param blockName - * @return - */ - private static ArrayList createSequenceBlock(String blockName) { - ArrayList blocks = new ArrayList<>(); - BlockBase b = create(blockName); - blocks.add(b); - return blocks; + private static List createRepetitionBlock(Class blockClass) { + return Arrays.asList(new RepetitionEndBlock(DriveApplication.getContext()), create(blockClass)); } - /** - * create a repetition block and an end block - * - * @param blockName - * @return - */ - private static ArrayList createRepetitionBlock( - String blockName) { - ArrayList blocks = new ArrayList<>(); - BlockBase b = new RepetitionEndBlock(DriveApplication.getContext()); // Add - // RepetitionEndBlock - blocks.add(b); - b = create(blockName); - blocks.add(b); - return blocks; + private static List createSelectionBlock(Class blockClass) { + return Arrays.asList(new SelectionEndBlock(DriveApplication.getContext()), create(blockClass)); } /** - * Create a selection block and an end block + * Returns one or two instances of block whose class is blockName. + * Type of block can be specified with @{howToMake}, which also determines the number + * of blocks this function returns. + * + * @param howToMake type of block. must be one of {@link BlockFactory#SEQUENCE}, + * {@link BlockFactory#REPETITION}, {@link BlockFactory#SELECTION}, + * and {@link BlockFactory#LOAD} + * @param blockName class name of block to be created + * @return list of blocks. the first element of the list is end block + * if block type is {@link BlockFactory#SELECTION} or + * {@link BlockFactory#REPETITION}. * - * @param blockName - * @return + * @exception RuntimeException if blockName is not supported block class name + * or howToMake is none of the values listed above */ - private static ArrayList createSelectionBlock(String blockName) { - ArrayList blocks = new ArrayList<>(); - BlockBase b = new SelectionEndBlock(DriveApplication.getContext()); // Add RepetitionEndBlock - blocks.add(b); - b = create(blockName); - blocks.add(b); - return blocks; + public static List createBlocks(@HowToMake int howToMake, String blockName) { + Class blockClass = getClassForName(blockName); + + switch (howToMake) { + case SEQUENCE: + case LOAD: { + return createSequenceBlock(blockClass); + } + + case REPETITION: { + return createRepetitionBlock(blockClass); + } + + case SELECTION: { + return createSelectionBlock(blockClass); + } + } + + throw new RuntimeException("Do not know how to make a block of " + blockName); } - public static ArrayList createBlocks(int howToMake, String blockName) { - ArrayList blocks = null; - if (howToMake == SEQUENCE) { - blocks = createSequenceBlock(blockName); - } else if (howToMake == REPETITION) { - blocks = createRepetitionBlock(blockName); - } else if (howToMake == SELECTION) { - blocks = createSelectionBlock(blockName); - } else if (howToMake == UNDO || howToMake == LOAD) { - // When users undo or load their program, this app should create - // a block. It is the same way when in which app makes sequence - // block - blocks = createSequenceBlock(blockName); - } - return blocks; + @IntDef({SEQUENCE, REPETITION, SELECTION, LOAD}) + public @interface HowToMake { } } diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/NumTextHolder.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/NumTextHolder.java deleted file mode 100644 index 1eb8811..0000000 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/NumTextHolder.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block; - -import android.widget.TextView; - -import com.pileproject.drive.util.development.Unit; - -/** - * Interface for block has TextView - * - * @author Tatsuya Iwanari - * @version 1.0 4-June-2013 - */ -public interface NumTextHolder { - /** - * Get number as Integer from TextView - * - * @return int - * Value of textView as integer - */ - int getNum(); - - /** - * Set number to TextView - * - * @param num the number that is set to TextView - */ - void setNum(int num); - - /** - * Get TextView - * - * @return TextView - */ - TextView getTextView(); - - /** - * Get Digit of the number of this block - * - * @return Integer[] - * 0 - Integral Part, 1 - Decimal Part - */ - Integer[] getDigit(); - - /** - * Get Max Value of this block - * - * @return double - * Maximum value - */ - double getMax(); - - /** - * Get Min Value of this block - * - * @return double - * Minimum value - */ - double getMin(); - - /** - * Returns unit of the value which is contained in this text field - * - * @return - */ - Unit getUnit(); -} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/NumberTextHolder.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/NumberTextHolder.java new file mode 100644 index 0000000..f991a55 --- /dev/null +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/NumberTextHolder.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block; + +import android.view.View; + +import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; + +import java.math.BigDecimal; + +/** + * Interface for block has TextView + * + * @author Tatsuya Iwanari + * @version 1.0 4-June-2013 + */ +public interface NumberTextHolder { + /** + * Get value of the TextView + */ + BigDecimal getValue(); + + /** + * Set value to TextView + */ + void setValue(BigDecimal value); + + /** + * Represents the precision of the value this class holds in 10-base. + * Precision of a value means the length of decimal digits + * e.g., if returns 3, the value can be set at 0.001-basis + */ + int getPrecision(); + + /** + * Returns the range which users can set the value within + * @return + */ + Range getRange(); + + /** + * Returns unit of the value + */ + Unit getUnit(); + + /** + * Enable the TextView in this holder if enable is true. + * Disable the TextView in this holder if enable is false + */ + void enableTextView(boolean enabled); + + /** + * Set {@link android.view.View.OnLongClickListener} to the TextView + */ + void setOnLongClickTextViewListener(View.OnLongClickListener listener); + + /** + * Returns a string which represents the value in this view. + * The string is formatted in {@link java.util.Locale#US} ("." means the radix point) + */ + String getValueAsString(); + + /** + * Set the value as String. + * The string should be formateed as {@link java.util.Locale#US} + */ + void setValueAsString(String value); +} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/NumberTextViewDelegate.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/NumberTextViewDelegate.java new file mode 100644 index 0000000..d28cb7b --- /dev/null +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/NumberTextViewDelegate.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block; + +import android.view.View; +import android.widget.TextView; + +import com.pileproject.drive.util.string.NumberUtil; +import com.pileproject.drive.util.string.ParseUtil; + +import java.math.BigDecimal; +import java.util.Locale; + +/** + * This class represents a delegate for classes with a TextView. + * Most of the methods in {@link NumberTextHolder} are implemented in this class. + * + * The list of methods in {@link NumberTextHolder} which are not implemented is: + *
    + *
  • {@link NumberTextHolder#getPrecision()}
  • + *
  • {@link NumberTextHolder#getRange()}
  • + *
  • {@link NumberTextHolder#getUnit()}
  • + *
+ * + * These methods should be implemented in concrete classes. + */ +public class NumberTextViewDelegate { + + private final TextView textView; + private final int precision; + + private BigDecimal valueCache; + + public NumberTextViewDelegate(TextView textView, NumberTextHolder holder) { + this.precision = holder.getPrecision(); + this.textView = textView; + } + + public int getActionValue() { + return getValue().movePointRight(precision).intValue(); + } + + public BigDecimal getValue() { + if (valueCache == null) { + String value = (String) textView.getText(); + valueCache = ParseUtil.bigDecimalValueOf(value); + } + + return valueCache; + } + + public void setValue(BigDecimal value) { + valueCache = value; + textView.setText(NumberUtil.toString(value, precision)); + } + + public String getValueAsString() { + return getValue().toString(); + } + + public void setValueAsString(String value) { + setValue(ParseUtil.bigDecimalValueOf(value, Locale.US)); + } + + public void enableTextView(boolean enabled) { + textView.setFocusable(enabled); + textView.setFocusableInTouchMode(enabled); + textView.setEnabled(enabled); + } + + public void setOnTextViewLongClickListener(View.OnLongClickListener listener) { + textView.setOnLongClickListener(listener); + } +} diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/WhileForeverBlock.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/LoopBlock.java similarity index 74% rename from app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/WhileForeverBlock.java rename to app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/LoopBlock.java index ae08248..c324d10 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/WhileForeverBlock.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/LoopBlock.java @@ -17,7 +17,6 @@ package com.pileproject.drive.programming.visual.block.repetition; import android.content.Context; -import android.view.LayoutInflater; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; @@ -29,17 +28,15 @@ * @author Tatsuya Iwanari * @version 1.0 7-July-2013 */ -public class WhileForeverBlock extends RepetitionBlock { +public class LoopBlock extends RepetitionBlock { - public WhileForeverBlock(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.block_while_forever, this); + public LoopBlock(Context context) { + super(context, R.layout.block_loop); } @Override public int action(MachineController controller, ExecutionCondition condition) { - int index = condition.getProgramCount(); - condition.pushBeginningOfLoop(index + FOREVER_WHILE_OFFSET); // push with offset + condition.enterInfiniteLoop(); return 1; } } diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/WhileNumBlock.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/NTimesBlock.java similarity index 58% rename from app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/WhileNumBlock.java rename to app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/NTimesBlock.java index c6497e0..2782b7a 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/WhileNumBlock.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/NTimesBlock.java @@ -17,14 +17,14 @@ package com.pileproject.drive.programming.visual.block.repetition; import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; + +import java.math.BigDecimal; /** * While in selected times @@ -32,50 +32,38 @@ * @author Tatsuya Iwanari * @version 1.0 7-July-2013 */ -public class WhileNumBlock extends RepetitionHasNumText { +public class NTimesBlock extends RepetitionBlockHasNumberText { - public WhileNumBlock(Context context) { - super(context); + // TODO: set from preference + private static final Range range = Range.closed(BigDecimal.ONE, new BigDecimal(5)); - View layout = LayoutInflater.from(context).inflate(R.layout.block_while_num, this); - numText = (TextView) layout.findViewById(R.id.block_numText); - } + private static final int PRECISION = 0; - @Override - public int getNum() { - return Integer.parseInt(numText.getText().toString()); + public NTimesBlock(Context context) { + super(context, R.layout.block_n_times, R.id.block_numText); } @Override - public void setNum(int num) { - numText.setText(String.valueOf(num)); - } - - @Override - public Integer[] getDigit() { - return new Integer[]{2, 0}; - } + public int action(MachineController controller, ExecutionCondition condition) { + int n = getValue().intValue(); + condition.enterNTimesLoop(n); - @Override - public double getMax() { - return (double) 5; + return 0; } @Override - public double getMin() { - return (double) 1; + public int getPrecision() { + return PRECISION; } @Override - public int action(MachineController controller, ExecutionCondition condition) { - int index = condition.getProgramCount(); - for (int i = 1; i < getNum(); i++) - condition.pushBeginningOfLoop(index); - return 0; + public Range getRange() { + return range; } @Override public Unit getUnit() { return Unit.NumberOfTimes; } + } diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBlock.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBlock.java index 85bc65e..3139b36 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBlock.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBlock.java @@ -17,6 +17,7 @@ package com.pileproject.drive.programming.visual.block.repetition; import android.content.Context; +import android.support.annotation.LayoutRes; import com.pileproject.drive.programming.visual.block.BlockBase; @@ -29,14 +30,13 @@ */ public abstract class RepetitionBlock extends BlockBase { - public static final int FOREVER_WHILE_OFFSET = -1000; - public RepetitionBlock(Context context) { - super(context); + public RepetitionBlock(Context context, @LayoutRes int layoutRes) { + super(context, layoutRes); } @Override - public Class getKind() { - return RepetitionBlock.class; + public final BlockKind getKind() { + return BlockKind.REPETITION_BEGIN; } } \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBlockHasNumberText.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBlockHasNumberText.java new file mode 100644 index 0000000..b553f77 --- /dev/null +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBlockHasNumberText.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block.repetition; + +import android.content.Context; +import android.support.annotation.IdRes; +import android.support.annotation.LayoutRes; +import android.view.View; +import android.widget.TextView; + +import com.pileproject.drive.programming.visual.block.NumberTextHolder; +import com.pileproject.drive.programming.visual.block.NumberTextViewDelegate; + +import java.math.BigDecimal; + +public abstract class RepetitionBlockHasNumberText extends RepetitionBlock implements NumberTextHolder { + + private final NumberTextViewDelegate mDelegate; + + /** + * Constructor + * + * @param context The context of activity that creates this view + */ + public RepetitionBlockHasNumberText(Context context, @LayoutRes int layoutRes, @IdRes int textViewId) { + super(context, layoutRes); + + mDelegate = new NumberTextViewDelegate((TextView) findViewById(textViewId), this); + } + + @Override + public BigDecimal getValue() { + return mDelegate.getValue(); + } + + @Override + public void setValue(BigDecimal value) { + mDelegate.setValue(value); + } + + @Override + public String getValueAsString() { + return mDelegate.getValueAsString(); + } + + @Override + public void setValueAsString(String value) { + mDelegate.setValueAsString(value); + } + + @Override + public void enableTextView(boolean enabled) { + mDelegate.enableTextView(enabled); + } + + @Override + public void setOnLongClickTextViewListener(View.OnLongClickListener listener) { + mDelegate.setOnTextViewLongClickListener(listener); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBreakBlock.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBreakBlock.java index 008dee3..90e8631 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBreakBlock.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionBreakBlock.java @@ -17,7 +17,6 @@ package com.pileproject.drive.programming.visual.block.repetition; import android.content.Context; -import android.view.LayoutInflater; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; @@ -29,13 +28,12 @@ */ public class RepetitionBreakBlock extends BlockBase { public RepetitionBreakBlock(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.block_repetition_break, this); + super(context, R.layout.block_repetition_break); } @Override - public Class getKind() { - return RepetitionBreakBlock.class; + public final BlockKind getKind() { + return BlockKind.REPETITION_BREAK; } @Override diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionEndBlock.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionEndBlock.java index 2058f3b..f82f841 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionEndBlock.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionEndBlock.java @@ -17,7 +17,6 @@ package com.pileproject.drive.programming.visual.block.repetition; import android.content.Context; -import android.view.LayoutInflater; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; @@ -33,13 +32,12 @@ public class RepetitionEndBlock extends BlockBase { public RepetitionEndBlock(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.block_repetition_end, this); + super(context, R.layout.block_repetition_end); } @Override - public Class getKind() { - return RepetitionEndBlock.class; + public final BlockKind getKind() { + return BlockKind.REPETITION_END; } @Override diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionHasNumText.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionHasNumText.java deleted file mode 100644 index 58c34b6..0000000 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/repetition/RepetitionHasNumText.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block.repetition; - -import android.content.Context; -import android.widget.TextView; - -import com.pileproject.drive.programming.visual.block.NumTextHolder; - - -public abstract class RepetitionHasNumText extends RepetitionBlock implements NumTextHolder { - - protected TextView numText; - - /** - * Constructor - * - * @param context The context of activity that creates this view - */ - public RepetitionHasNumText(Context context) { - super(context); - } - - @Override - public TextView getTextView() { - return numText; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionBlock.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionBlock.java index d4850e7..b25dcfa 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionBlock.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionBlock.java @@ -17,7 +17,10 @@ package com.pileproject.drive.programming.visual.block.selection; import android.content.Context; +import android.support.annotation.LayoutRes; +import com.pileproject.drive.execution.ExecutionCondition; +import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.programming.visual.block.BlockBase; /** @@ -28,12 +31,21 @@ */ public abstract class SelectionBlock extends BlockBase { - public SelectionBlock(Context context) { - super(context); + public SelectionBlock(Context context, @LayoutRes int layoutRes) { + super(context, layoutRes); } + protected abstract boolean evaluateCondition(MachineController controller); + @Override - public Class getKind() { - return SelectionBlock.class; + public final BlockKind getKind() { + return BlockKind.SELECTION_BEGIN; } + + @Override + public final int action(MachineController controller, ExecutionCondition condition) { + condition.pushSelectionResult(evaluateCondition(controller)); + return 0; + } + } \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionBlockHasNumberText.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionBlockHasNumberText.java new file mode 100644 index 0000000..3cc7b75 --- /dev/null +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionBlockHasNumberText.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block.selection; + +import android.content.Context; +import android.support.annotation.IdRes; +import android.support.annotation.LayoutRes; +import android.view.View; +import android.widget.TextView; + +import com.pileproject.drive.programming.visual.block.NumberTextHolder; +import com.pileproject.drive.programming.visual.block.NumberTextViewDelegate; + +import java.math.BigDecimal; + +/** + * SelectionBlock that has a TextView + * + * @author Tatsuya Iwanari + * @version 1.0 7-July-2013 + */ +public abstract class SelectionBlockHasNumberText extends SelectionBlock implements NumberTextHolder { + + private final NumberTextViewDelegate mDelegate; + + public SelectionBlockHasNumberText(Context context, @LayoutRes int layoutRes, @IdRes int textViewId) { + super(context, layoutRes); + + mDelegate = new NumberTextViewDelegate((TextView) findViewById(textViewId), this); + } + + @Override + public BigDecimal getValue() { + return mDelegate.getValue(); + } + + @Override + public void setValue(BigDecimal value) { + mDelegate.setValue(value); + } + + @Override + public String getValueAsString() { + return mDelegate.getValueAsString(); + } + + @Override + public void setValueAsString(String value) { + mDelegate.setValueAsString(value); + } + + @Override + public void enableTextView(boolean enabled) { + mDelegate.enableTextView(enabled); + } + + @Override + public void setOnLongClickTextViewListener(View.OnLongClickListener listener) { + mDelegate.setOnTextViewLongClickListener(listener); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionEndBlock.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionEndBlock.java index e54d1a5..8559d12 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionEndBlock.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionEndBlock.java @@ -17,7 +17,6 @@ package com.pileproject.drive.programming.visual.block.selection; import android.content.Context; -import android.view.LayoutInflater; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; @@ -31,13 +30,12 @@ public class SelectionEndBlock extends BlockBase { public SelectionEndBlock(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.block_selection_end, this); + super(context, R.layout.block_selection_end); } @Override - public Class getKind() { - return SelectionEndBlock.class; + public BlockKind getKind() { + return BlockKind.SELECTION_END; } @Override diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionHasNumText.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionHasNumText.java deleted file mode 100644 index 35ce221..0000000 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/selection/SelectionHasNumText.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block.selection; - -import android.content.Context; -import android.widget.TextView; - -import com.pileproject.drive.programming.visual.block.NumTextHolder; - -/** - * SelectionBlock that has a TextView - * - * @author Tatsuya Iwanari - * @version 1.0 7-July-2013 - */ -public abstract class SelectionHasNumText extends SelectionBlock implements NumTextHolder { - - protected TextView numText; - - public SelectionHasNumText(Context context) { - super(context); - } - - @Override - public TextView getTextView() { - return numText; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlock.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlock.java index 0f57f17..5a36158 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlock.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlock.java @@ -17,6 +17,7 @@ package com.pileproject.drive.programming.visual.block.sequence; import android.content.Context; +import android.support.annotation.LayoutRes; import com.pileproject.drive.programming.visual.block.BlockBase; @@ -28,12 +29,12 @@ */ public abstract class SequenceBlock extends BlockBase { - public SequenceBlock(Context context) { - super(context); + public SequenceBlock(Context context, @LayoutRes int layoutRes) { + super(context, layoutRes); } @Override - public Class getKind() { - return SequenceBlock.class; + public final BlockKind getKind() { + return BlockKind.SEQUENCE; } } \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlockHasNumText.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlockHasNumText.java deleted file mode 100644 index 3fafb49..0000000 --- a/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlockHasNumText.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block.sequence; - -import android.content.Context; -import android.widget.TextView; - -import com.pileproject.drive.programming.visual.block.NumTextHolder; - - -/** - * SequenceBlock that has a TextView - * - * @author Tatsuya Iwanari - * @version 1.0 7-July-2013 - */ -public abstract class SequenceBlockHasNumText extends SequenceBlock implements NumTextHolder { - - protected TextView numText; - - public SequenceBlockHasNumText(Context context) { - super(context); - } - - @Override - public TextView getTextView() { - return numText; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlockHasNumberText.java b/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlockHasNumberText.java new file mode 100644 index 0000000..b25e2c3 --- /dev/null +++ b/app/src/main/java/com/pileproject/drive/programming/visual/block/sequence/SequenceBlockHasNumberText.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.programming.visual.block.sequence; + +import android.content.Context; +import android.support.annotation.IdRes; +import android.support.annotation.LayoutRes; +import android.view.View; +import android.widget.TextView; + +import com.pileproject.drive.programming.visual.block.NumberTextHolder; +import com.pileproject.drive.programming.visual.block.NumberTextViewDelegate; + +import java.math.BigDecimal; + + +/** + * SequenceBlock that has a TextView + * + * @author Tatsuya Iwanari + * @version 1.0 7-July-2013 + */ +public abstract class SequenceBlockHasNumberText extends SequenceBlock implements NumberTextHolder { + + private final NumberTextViewDelegate mDelegate; + + public SequenceBlockHasNumberText(Context context, @LayoutRes int layoutRes, @IdRes int textViewId) { + super(context, layoutRes); + + mDelegate = new NumberTextViewDelegate((TextView) findViewById(textViewId), this); + } + + /** + * Returns the value for those which do not understand radix points + */ + protected int getActionValue() { + return mDelegate.getActionValue(); + } + + @Override + public BigDecimal getValue() { + return mDelegate.getValue(); + } + + @Override + public void setValue(BigDecimal value) { + mDelegate.setValue(value); + } + + @Override + public String getValueAsString() { + return mDelegate.getValueAsString(); + } + + @Override + public void setValueAsString(String value) { + mDelegate.setValueAsString(value); + } + + @Override + public void enableTextView(boolean enabled) { + mDelegate.enableTextView(enabled); + } + + @Override + public void setOnLongClickTextViewListener(View.OnLongClickListener listener) { + mDelegate.setOnTextViewLongClickListener(listener); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/layout/BlockSpaceManagerBase.java b/app/src/main/java/com/pileproject/drive/programming/visual/layout/BlockSpaceManagerBase.java index 82c0b11..cbef4c0 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/layout/BlockSpaceManagerBase.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/layout/BlockSpaceManagerBase.java @@ -23,6 +23,7 @@ import com.pileproject.drive.programming.visual.block.BlockBase; import java.util.ArrayList; +import java.util.List; /** * A manager of BlockSpaceLayout. @@ -126,9 +127,9 @@ public ArrayList loadUserProgramNames() { * e.g., can move or not * @param blocks */ - public abstract void addBlocks(ArrayList blocks); + public abstract void addBlocks(List blocks); - private void placeBlocks(ArrayList data) { + private void placeBlocks(List data) { if (data.isEmpty()) { return; } diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/layout/ExecutionSpaceManager.java b/app/src/main/java/com/pileproject/drive/programming/visual/layout/ExecutionSpaceManager.java index 17b1994..2eb4d06 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/layout/ExecutionSpaceManager.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/layout/ExecutionSpaceManager.java @@ -18,13 +18,12 @@ import android.content.Context; import android.view.View; import android.widget.LinearLayout.LayoutParams; -import android.widget.TextView; import com.pileproject.drive.programming.visual.block.BlockBase; -import com.pileproject.drive.programming.visual.block.NumTextHolder; +import com.pileproject.drive.programming.visual.block.NumberTextHolder; import com.pileproject.drive.view.FrameView; -import java.util.ArrayList; +import java.util.List; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; @@ -40,15 +39,14 @@ public ExecutionSpaceManager(Context context, BlockSpaceLayout layout) { } @Override - public void addBlocks(ArrayList blocks) { + public void addBlocks(List blocks) { + for (BlockBase block : blocks) { - if (block instanceof NumTextHolder) { - // set views not editable - TextView numText = ((NumTextHolder) block).getTextView(); - numText.setFocusable(false); - numText.setFocusableInTouchMode(false); - numText.setEnabled(false); + + if (block instanceof NumberTextHolder) { + ((NumberTextHolder) block).enableTextView(false); } + mLayout.addView(block, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); } } diff --git a/app/src/main/java/com/pileproject/drive/programming/visual/layout/ProgrammingSpaceManager.java b/app/src/main/java/com/pileproject/drive/programming/visual/layout/ProgrammingSpaceManager.java index 31bc81b..9ebf231 100644 --- a/app/src/main/java/com/pileproject/drive/programming/visual/layout/ProgrammingSpaceManager.java +++ b/app/src/main/java/com/pileproject/drive/programming/visual/layout/ProgrammingSpaceManager.java @@ -19,26 +19,21 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnLongClickListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.view.animation.CycleInterpolator; import android.widget.LinearLayout.LayoutParams; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.programming.visual.block.BlockBase; -import com.pileproject.drive.programming.visual.block.NumTextHolder; -import com.pileproject.drive.util.development.Unit; -import com.pileproject.drive.util.math.Range; +import com.pileproject.drive.programming.visual.block.NumberTextHolder; import com.pileproject.drive.view.NumberSelectSeekBarView; import com.pileproject.drive.view.NumberSelectViewBase; -import java.util.ArrayList; +import java.util.List; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; @@ -46,7 +41,38 @@ * A manager of BlockSpaceLayout that helps users to make programs. */ public class ProgrammingSpaceManager extends BlockSpaceManagerBase { - public final OnTouchListener mMoveBlock = new OnTouchListener() { + + private final BlockOnTouchListener mOnTouchListener = new BlockOnTouchListener(); + + public ProgrammingSpaceManager(Context context, BlockSpaceLayout layout) { + super(context, layout); + } + + private void setListeners(BlockBase block) { + // set OnTouchListener to the block + block.setOnTouchListener(mOnTouchListener); + + if (block instanceof NumberTextHolder) { + ((NumberTextHolder) block).setOnLongClickTextViewListener( + new OnTouchNumberTextListener((NumberTextHolder) block)); + } + } + + @Override + public void addBlocks(List blocks) { + // emphasize block animation + AlphaAnimation alpha = new AlphaAnimation(1, 0); + alpha.setDuration(1000); + alpha.setInterpolator(new CycleInterpolator(3)); + + for (BlockBase block : blocks) { + setListeners(block); + mLayout.addView(block, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + block.setAnimation(alpha); + } + } + + private class BlockOnTouchListener implements OnTouchListener { int currentX; // the left position of this view (x coordinate) int currentY; // the top position of this view (y coordinate) int offsetX; // the x position of user's finger @@ -115,85 +141,33 @@ private void moveViewWithinItsParent(View view, int currentX, int currentY) { view.layout(currentX, currentY, currentX + view.getWidth(), currentY + view.getHeight()); } - }; - - public ProgrammingSpaceManager(Context context, BlockSpaceLayout layout) { - super(context, layout); } - private void setListeners(BlockBase block) { - // set OnTouchListener to the block - block.setOnTouchListener(mMoveBlock); - // set OnTouchListener to TextView - if (block instanceof NumTextHolder) { - TextView numText = ((NumTextHolder) block).getTextView(); - numText.setOnLongClickListener(new OnTouchNumTextListener(block)); - } - } + private class OnTouchNumberTextListener implements View.OnLongClickListener { - @Override - public void addBlocks(ArrayList blocks) { - // emphasize block animation - AlphaAnimation alpha = new AlphaAnimation(1, 0); - alpha.setDuration(1000); - alpha.setInterpolator(new CycleInterpolator(3)); + private final NumberTextHolder mHolder; - for (BlockBase block : blocks) { - setListeners(block); - mLayout.addView(block, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - block.setAnimation(alpha); + public OnTouchNumberTextListener(NumberTextHolder parent) { + mHolder = parent; } - } - @Override - public void deleteAllBlocks() { - super.deleteAllBlocks(); - } + @Override + public boolean onLongClick(View v) { - /** - * A listener to pick a value. - */ - class OnTouchNumTextListener implements OnLongClickListener { - NumTextHolder mParent; + final NumberSelectViewBase numberSelectView = new NumberSelectSeekBarView(mContext, + mHolder.getValue(), mHolder.getRange(), mHolder.getPrecision(), mHolder.getUnit()); - public OnTouchNumTextListener(BlockBase parent) { - mParent = (NumTextHolder) parent; - } + new AlertDialog.Builder(mContext) + .setTitle(R.string.programming_pleaseSelectNumbers) + .setView(numberSelectView) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mHolder.setValue(numberSelectView.getValue()); + } + }) + .show(); - @Override - public boolean onLongClick(View v) { - // get the old value - final int oldNum = mParent.getNum(); - - // create a new NumberPicker and set the old value - Integer[] digit = mParent.getDigit(); - - final int numOfIntegralDigits = digit[0]; - final int numOfDecimalDigits = digit[1]; - - final Range range = Range.closed(mParent.getMin(), mParent.getMax()); - final Unit unit = mParent.getUnit(); - - final NumberSelectViewBase numberSelectView = - new NumberSelectSeekBarView(mContext, range, unit, numOfIntegralDigits, numOfDecimalDigits); - - numberSelectView.setNum(oldNum); - - // create a new AlertDialog to pick the number - AlertDialog.Builder dialog = new AlertDialog.Builder(mContext); - dialog.setTitle(R.string.programming_pleaseSelectNumbers); - dialog.setView(numberSelectView); - dialog.setPositiveButton(R.string.ok, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - double rawNum = numberSelectView.getSelectedNum(); - int newNum = (int) (rawNum * Math.pow(10, numOfDecimalDigits)); - - // set new value - mParent.setNum(newNum); - } - }); - dialog.show(); return true; } } diff --git a/app/src/main/java/com/pileproject/drive/util/bluetooth/BluetoothUtil.java b/app/src/main/java/com/pileproject/drive/util/bluetooth/BluetoothUtil.java index 828be9e..276ab1f 100644 --- a/app/src/main/java/com/pileproject/drive/util/bluetooth/BluetoothUtil.java +++ b/app/src/main/java/com/pileproject/drive/util/bluetooth/BluetoothUtil.java @@ -19,6 +19,11 @@ import android.bluetooth.BluetoothAdapter; public class BluetoothUtil { + + private BluetoothUtil() { + throw new AssertionError("This class cannot be instantiated"); + } + public static boolean hasBluetoothFunction() { return BluetoothAdapter.getDefaultAdapter() != null; } diff --git a/app/src/main/java/com/pileproject/drive/util/development/DeployUtils.java b/app/src/main/java/com/pileproject/drive/util/development/DeployUtils.java index 2b8de8f..e403f5f 100644 --- a/app/src/main/java/com/pileproject/drive/util/development/DeployUtils.java +++ b/app/src/main/java/com/pileproject/drive/util/development/DeployUtils.java @@ -26,6 +26,11 @@ * A class for checking building environments, devices, etc. */ public class DeployUtils { + + private DeployUtils() { + throw new AssertionError("This class cannot be instantiated"); + } + /** * Return true if the device is emulator. */ diff --git a/app/src/main/java/com/pileproject/drive/util/development/Unit.java b/app/src/main/java/com/pileproject/drive/util/development/Unit.java index 7314a2a..6a9faa3 100644 --- a/app/src/main/java/com/pileproject/drive/util/development/Unit.java +++ b/app/src/main/java/com/pileproject/drive/util/development/Unit.java @@ -16,66 +16,66 @@ package com.pileproject.drive.util.development; -import android.content.Context; +import android.content.res.Resources; import com.pileproject.drive.R; +import com.pileproject.drive.app.DriveApplication; +import com.pileproject.drive.util.string.NumberUtil; + +import java.util.Locale; /** * enumeration of unit for number selecting on blocks * * @author yusaku */ -public enum Unit { - Second, - Percentage, - NumberOfTimes, - Dimensionless; +public class Unit { - /** - * get the unit string based on the value and selected unit - * - * @return string which contains the value and its unit - */ - public static String getUnitString( - Context context, Unit unit, String format, double value) { - final String formattedValue = String.format(format, value); + public static final Unit Second = new Unit(); + public static final Unit Percentage = new Unit(); + public static final Unit NumberOfTimes = new Unit(); - final int quantity = getQuantityOfValueInStringFormat(formattedValue, format); + private static final Resources RESOURCES = DriveApplication.getContext().getResources(); - switch (unit) { - case Second: - return value + " " + context.getResources().getQuantityString(R.plurals.seconds, quantity); + private Unit() { - case Percentage: - return value + " " + context.getResources().getString(R.string.percent); - - case NumberOfTimes: - return context.getResources() - .getString(R.string.blocks_repeatNum, - Integer.parseInt(formattedValue.trim())); - - default: - break; - } + } - return ""; + /** + * Decorates numeric value with the Unit string with precision. + * This method takes locales into account and uses default locale by calling + * {@link Locale#getDefault()}. + * + * @param value value to be decorated + * @param precision precision of the value in the return string. + * @param type parameter for value, which extends {@link Number} + * @return decorated string in default locale and with given precision. + */ + public String decorateValue(T value, int precision) { + return decorateValue(Locale.getDefault(), value, precision); } /** - * returns a quantity of formattedValue - * TODO: apply this for other locale than English - * See also: http://www.unicode - * .org/cldr/charts/latest/supplemental/language_plural_rules.html + * Decorates numeric value with the Unit string with precision. + * This method takes locales into account. * - * @param formattedValue - * @param format - * @return + * @param locale the locale in which the value decorated + * @param value value to be decorated + * @param precision precision of the value in the return string. + * @param type parameter for value, which extends {@link Number} + * @return decorated string in default locale and with given precision. */ - private static int getQuantityOfValueInStringFormat(String formattedValue, String format) { - if (formattedValue.equals(String.format(format, 1.0))) { - return 1; + public String decorateValue(Locale locale, T value, int precision) { + + if (this == Second) { + return NumberUtil.toString(locale, value, precision) + RESOURCES.getString(R.string.second); + } + + if (this == Percentage) { + return NumberUtil.toString(locale, value, precision) + RESOURCES.getString(R.string.percent); } - return 2; + // if this == NumberOfTimes + return RESOURCES.getString(R.string.blocks_repeatNum, (Integer) value); } } diff --git a/app/src/main/java/com/pileproject/drive/util/string/NumberUtil.java b/app/src/main/java/com/pileproject/drive/util/string/NumberUtil.java new file mode 100644 index 0000000..41966cc --- /dev/null +++ b/app/src/main/java/com/pileproject/drive/util/string/NumberUtil.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.util.string; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +public class NumberUtil { + + private NumberUtil() { + throw new AssertionError("This class cannot be instantiated"); + } + + /** + * Converts numerical value to string expression. This function takes locales into account and + * uses default locale by calling {@link Locale#getDefault()}. + * With precision in the arguments you can control the number of digits in fraction + * part. + * + * @param value value to be converted as a string + * @param precision number of precision. + * @param type parameter of value, which should be extends {@link Number} + * @return string expression of value in appropriate locale and + * with precision + */ + public static String toString(T value, int precision) { + return toString(Locale.getDefault(), value, precision); + } + + /** + * Converts numerical value to string expression. This function takes locales into account. + * With precision in the arguments you can control the number of digits in fraction + * part. + * + * @param locale locale in which the value will be expressed. + * @param value value to be converted as a string + * @param precision number of precision. + * @param type parameter of value, which should be extends {@link Number} + * @return string expression of value in appropriate locale and + * with precision + */ + public static String toString(Locale locale, T value, int precision) { + DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(locale); + + decimalFormat.setMaximumFractionDigits(precision); + decimalFormat.setMinimumFractionDigits(precision); + + return decimalFormat.format(value); + } +} diff --git a/app/src/main/java/com/pileproject/drive/util/string/ParseUtil.java b/app/src/main/java/com/pileproject/drive/util/string/ParseUtil.java new file mode 100644 index 0000000..6ce345d --- /dev/null +++ b/app/src/main/java/com/pileproject/drive/util/string/ParseUtil.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.util.string; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.Locale; + +public class ParseUtil { + + private ParseUtil() { + throw new AssertionError("This class cannot be instantiated"); + } + + /** + * Parse a string as double in the context of default locale + * (e.g., "1,234" means 1234E-3 in France). + * + * @throws NumberFormatException throw if the string makes no sense as number. + */ + public static double doubleValueOf(String value) { + return doubleValueOf(value, Locale.getDefault()); + } + + /** + * Parse a string as double. + * This function takes locale into account (e.g., "1,234" means 1234E-3 in France). + * + * @throws NumberFormatException throw if the string makes no sense as number. + */ + public static double doubleValueOf(String value, Locale locale) throws NumberFormatException { + try { + NumberFormat format = NumberFormat.getNumberInstance(locale); + Number number = format.parse(value); + return number.doubleValue(); + } catch (ParseException e) { + throw new NumberFormatException("String '" + value + "' cannot be parsed as double value" + + " in locale " + locale); + } + } + + /** + * Parse a string as {@link BigDecimal} in the context of default locale + * (e.g., "1,234" means 1234E-3 in France). + * + * @throws NumberFormatException throw if the string makes no sense as number. + */ + public static BigDecimal bigDecimalValueOf(String value) throws NumberFormatException { + return bigDecimalValueOf(value, Locale.getDefault()); + } + + /** + * Parse a string as {@link BigDecimal}. + * This function takes locale into account (e.g., "1,234" means 1234E-3 in France). + * + * @throws NumberFormatException throw if the string makes no sense as number. + */ + public static BigDecimal bigDecimalValueOf(String value, Locale locale) throws NumberFormatException { + try { + DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(locale); + decimalFormat.setParseBigDecimal(true); + Number number = decimalFormat.parse(value); + return (BigDecimal) number; + } catch (ParseException e) { + throw new NumberFormatException("String '" + value + "' cannot be parsed as double value" + + " in locale " + locale); + } + } +} diff --git a/app/src/main/java/com/pileproject/drive/view/NumberSelectSeekBarView.java b/app/src/main/java/com/pileproject/drive/view/NumberSelectSeekBarView.java index f2245a1..f918e38 100644 --- a/app/src/main/java/com/pileproject/drive/view/NumberSelectSeekBarView.java +++ b/app/src/main/java/com/pileproject/drive/view/NumberSelectSeekBarView.java @@ -18,13 +18,14 @@ import android.content.Context; import android.view.LayoutInflater; -import android.view.View; import android.widget.SeekBar; import android.widget.TextView; import com.pileproject.drive.R; -import com.pileproject.drive.util.math.Range; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; + +import java.math.BigDecimal; /** * A view class which is used for number selecting on blocks. @@ -32,28 +33,19 @@ */ public class NumberSelectSeekBarView extends NumberSelectViewBase { - private final int mNumberOfIntegralDigits; - private final int mNumberOfDecimalDigits; + private final Range mRange; + private final int mPrecision; + private final SeekBar mSeekBar; private final Unit mUnit; - - private double mValue; - - private SeekBar mSeekBar; - private TextView mTextView; - + private final TextView mTextView; + private BigDecimal mValue; private final SeekBar.OnSeekBarChangeListener mListener = new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekbar, int value, boolean fromUser) { - // the value should be jacked up by the apparent minimum value of SeekBar - // when the value is displayed - mValue = toDoubleExpression(value) + mRange.getLowerBound(); + mValue = fromSeekBarExpression(value, mRange, mPrecision); - final String fmt = - "%" + mNumberOfIntegralDigits + "." + mNumberOfDecimalDigits + "f"; - - // use the proper unit based on the value - mTextView.setText(Unit.getUnitString(mContext, mUnit, fmt, mValue)); + mTextView.setText(mUnit.decorateValue(mValue, mPrecision)); } @Override @@ -68,72 +60,79 @@ public void onStopTrackingTouch(SeekBar arg0) { }; /** - * @param context - * @param range - a range users can select within this range - * @param unit - a unit of the value in this view - * @param numOfIntegralDigits - number of integral digits - * @param numOfDecimalDigits - number of decimal digits + * Constructor. + * @param context context + * @param value initial value + * @param range BigDecimal range. users can select within this range + * @param precision precision of the value + * @param unit unit of the value in this view */ - public NumberSelectSeekBarView(Context context, Range range, - Unit unit, int numOfIntegralDigits, int numOfDecimalDigits) { - super(context, range); + public NumberSelectSeekBarView(Context context, BigDecimal value, Range range, + int precision, Unit unit) { + super(context); mUnit = unit; + mRange = range; + mPrecision = precision; - mNumberOfIntegralDigits = numOfIntegralDigits; - mNumberOfDecimalDigits = numOfDecimalDigits; + LayoutInflater.from(context).inflate(R.layout.view_number_select_seekbar, this); + mTextView = (TextView) findViewById(R.id.programming_numberSelectView_valueText); + mSeekBar = (SeekBar) findViewById(R.id.programming_numberSelectView_seekBar); - View layout = LayoutInflater.from(context).inflate(R.layout.view_number_select_seekbar, this); + mSeekBar.setOnSeekBarChangeListener(mListener); + mSeekBar.setMax(toSeekBarExpression(mRange.getUpperBound(), mRange, precision)); - mTextView = (TextView) layout.findViewById(R.id.programming_numberSelectView_valueText); - mSeekBar = (SeekBar) layout.findViewById(R.id.programming_numberSelectView_seekBar); + setValue(value); + } - mSeekBar.setOnSeekBarChangeListener(mListener); + private static int toSeekBarExpression(BigDecimal value, Range range, int precision) { + + if (!range.contains(value)) { + return 0; + } - int min = toIntegerExpression(mRange.getLowerBound()); - int max = toIntegerExpression(mRange.getUpperBound()); + // if value = 0.2, range = (-1.0, 1.0), precision = 1, we want to get a result 12 + // because in seekbar expression, values in the range are converted as below + // (-1.0, -0.9, ..., 0.0, ..., 0.9, 1.0) => (0, 1, ..., 10, ..., 19, 20) - mSeekBar.setMax(max - min); + // so the following operations are needed: + // -1.0 => -10 + BigDecimal lowerMoved = range.getLowerBound().movePointRight(precision); + // 0.2 => 2 + BigDecimal valueMoved = value.movePointRight(precision); + // 2 - (-10) = 12 + BigDecimal ret = valueMoved.subtract(lowerMoved); + + return ret.intValue(); } - @Override - public void setNum(int num) { - double value = toDoubleExpression(num); + private static BigDecimal fromSeekBarExpression(int value, Range range, int precision) { - if (!mRange.contains(value)) { + BigDecimal valueMoved = new BigDecimal(value).movePointLeft(precision); + BigDecimal ret = valueMoved.add(range.getLowerBound()); + + if (!range.contains(ret)) { throw new RuntimeException("The value to set SeekBar is out of range " + - "(range: " + mRange + ", value: " + value + ")"); + "(range: " + range + ", value: " + value + ")"); } - // because minimum value of SeekBar is always 0, - // the value to set SeekBar should be subtracted by its "apparent" minimum - mValue = value - mRange.getLowerBound(); - - mSeekBar.setProgress(toIntegerExpression(mValue)); + return ret; } @Override - public double getSelectedNum() { + public BigDecimal getValue() { return mValue; } - /** - * Get rid of the decimal part. - * e.g., 1.15 -> 115 - * @param value a value in double - * @return a value in integer - */ - private int toIntegerExpression(double value) { - return (int) (value * Math.pow(10.0, mNumberOfDecimalDigits)); - } + @Override + public void setValue(BigDecimal value) { - /** - * Convert an integer value to double value. - * e.g., 115 -> 1.15 - * @param value a value in integer - * @return a value in double - */ - private double toDoubleExpression(int value) { - return value / Math.pow(10.0, mNumberOfDecimalDigits); + if (!mRange.contains(value)) { + throw new RuntimeException("The value to set SeekBar is out of range " + + "(range: " + mRange + ", value: " + value + ")"); + } + + mValue = value; + mSeekBar.setProgress(toSeekBarExpression(mValue, mRange, mPrecision)); } } diff --git a/app/src/main/java/com/pileproject/drive/view/NumberSelectViewBase.java b/app/src/main/java/com/pileproject/drive/view/NumberSelectViewBase.java index 42d7fc6..d40cf8c 100644 --- a/app/src/main/java/com/pileproject/drive/view/NumberSelectViewBase.java +++ b/app/src/main/java/com/pileproject/drive/view/NumberSelectViewBase.java @@ -19,31 +19,27 @@ import android.content.Context; import android.widget.LinearLayout; -import com.pileproject.drive.util.math.Range; +import java.math.BigDecimal; /** * An abstract view class which provides an interface of a view for selecting values. */ public abstract class NumberSelectViewBase extends LinearLayout { - final protected Range mRange; - protected Context mContext; - public NumberSelectViewBase(Context context, Range range) { + public NumberSelectViewBase(Context context) { super(context); - mContext = context; - mRange = range; } - /** - * A setter of a value. - * @param num a value to be set - */ - public abstract void setNum(int num); - /** * A getter of a value. * This is used to get the real value in double. * @return the selected value */ - public abstract double getSelectedNum(); + public abstract BigDecimal getValue(); + + /** + * A setter of a value. + * @param value a value to be set + */ + public abstract void setValue(BigDecimal value); } diff --git a/app/src/main/java/com/pileproject/drive/view/NumberSelectWheelScrollerView.java b/app/src/main/java/com/pileproject/drive/view/NumberSelectWheelScrollerView.java index f064500..eafdc4f 100644 --- a/app/src/main/java/com/pileproject/drive/view/NumberSelectWheelScrollerView.java +++ b/app/src/main/java/com/pileproject/drive/view/NumberSelectWheelScrollerView.java @@ -26,6 +26,7 @@ import com.pileproject.drive.R; import com.pileproject.drive.util.math.Range; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -34,76 +35,132 @@ */ public class NumberSelectWheelScrollerView extends NumberSelectViewBase { private List mNumbers; - private int mIntegralPart, mDecimalPart; + + private final Range mRange; + private final int mScaledMaximum; + private final int mScaledMinimum; + + private final int mDigitCounts; + private final int mPrecision; + + private final OnValueChangeListener listener = new OnValueChangeListener() { + + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + + picker.setValue(newVal); + + int scaledValue = getScaledValue(); + + if (scaledValue > mScaledMaximum) { + changeToMax(); + } else if (scaledValue < mScaledMinimum) { + changeToMin(); + } + } + }; /** - * @param context an application context - * @param integralPart the length of integral part - * @param decimalPart the length of decimal part + * Constructor. + * @param context context + * @param value initial value + * @param range BigDecimal range. users can select within this range + * @param precision precision of the value + * @param digitCount number of digits this view has */ - public NumberSelectWheelScrollerView(Context context, Range range, int integralPart, int decimalPart) { - super(context, range); + public NumberSelectWheelScrollerView(Context context, BigDecimal value, Range range, int precision, int digitCount) { + super(context); + + mPrecision = precision; + mDigitCounts = digitCount; - final double min = range.getLowerBound(); - final double max = range.getUpperBound(); + mRange = range; + mScaledMaximum = mRange.getUpperBound().movePointRight(mPrecision).intValue(); + mScaledMinimum = mRange.getLowerBound().movePointRight(mPrecision).intValue(); - // choose somewhat correct lengths - mIntegralPart = (integralPart > 0) ? integralPart : 3; - mDecimalPart = (decimalPart >= 0) ? decimalPart : 0; - mNumbers = new ArrayList<>(mIntegralPart + mDecimalPart); + mNumbers = new ArrayList<>(mDigitCounts); // create LayoutParams (width = 0, height = wrap_content, weight = 1.0f) LayoutParams lp = new LayoutParams(0, FrameLayout.LayoutParams.WRAP_CONTENT); lp.weight = 1.0f; - for (int i = 0; i < mIntegralPart + mDecimalPart; i++) { - NumberPicker picker = new NumberPicker(context); - - // allow numbers 0 to 9 - picker.setMaxValue(9); - picker.setMinValue(0); - - // hide software keyboard - picker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); - picker.setOnValueChangedListener(new OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - int index = mNumbers.indexOf(picker); - picker.setValue(oldVal); // reset old values for check - - double raw = getSelectedNum(); - - // prevent overflow - if ((oldVal + 1) % 10 == newVal) { - // check the new value is larger than the maximum value or not - if (raw + Math.pow(10, mIntegralPart - (index + 1)) > max) { - changeToMax(); // change value to maximum - return; - } - } - // prevent underflow - else { - // check the new value is lower than the minimum value or not - if (raw - Math.pow(10, mIntegralPart - (index + 1)) < min) { - changeToMin(); // change value to minimum - return; - } - } - picker.setValue(newVal); - } - }); + for (int i = 0; i < mDigitCounts; i++) { + NumberPicker picker = createNumberPicker(context); + this.addView(picker, lp); mNumbers.add(picker); } - if (mDecimalPart != 0) { + if (mPrecision != 0) { // add period between integral part and decimal part TextView period = new TextView(context); period.setText(R.string.select_number_period); // period.setTextSize(R.dimen.period); lp.gravity = Gravity.CENTER; - this.addView(period, mIntegralPart, lp); + int insertIndex = mDigitCounts - mPrecision; + this.addView(period, insertIndex, lp); + } + + setValue(value); + } + + @Override + public BigDecimal getValue() { + return new BigDecimal(getScaledValue()).movePointLeft(mPrecision); + } + + @Override + public void setValue(BigDecimal value) { + setScaledValue(value.movePointRight(mPrecision).intValue()); + } + + /** + * Change the number of this view to maximum value. + */ + public void changeToMax() { + setScaledValue(mScaledMaximum); + } + + /** + * Change the number of this view to minimum value. + */ + public void changeToMin() { + setScaledValue(mScaledMinimum); + } + + private NumberPicker createNumberPicker(Context context) { + + NumberPicker picker = new NumberPicker(context); + + // allow numbers 0 to 9 + picker.setMaxValue(9); + picker.setMinValue(0); + + // hide software keyboard + picker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + picker.setOnValueChangedListener(listener); + + return picker; + } + + private int getScaledValue() { + int scaledResult = 0; + + for (int i = 0; i < mDigitCounts; ++i) { + int digit = mNumbers.get(mDigitCounts - (i + 1)).getValue(); + + scaledResult += digit * Math.pow(10, i); + } + + return scaledResult; + } + + private void setScaledValue(int scaledValue) { + for (int i = 0; i < mDigitCounts; ++i) { + int digit = (int) Math.pow(10, mDigitCounts - (i + 1)); + mNumbers.get(i).setValue(scaledValue / digit); + scaledValue %= digit; } } @@ -111,6 +168,7 @@ public void onValueChange(NumberPicker picker, int oldVal, int newVal) { * Carry up count. * @param index the index of the target number */ + @Deprecated public void carryUp(int index) { if (index < 0) { return; @@ -130,6 +188,7 @@ public void carryUp(int index) { * Carry down count. * @param index the index of the target number */ + @Deprecated public void carryDown(int index) { if (index < 0) { return; @@ -144,47 +203,4 @@ public void carryDown(int index) { picker.setValue(picker.getValue() - 1); } } - - @Override - public double getSelectedNum() { - double result = 0; - for (int i = 0; i < mIntegralPart + mDecimalPart; i++) - result += mNumbers.get(i).getValue() * Math.pow(10, mIntegralPart - (i + 1)); - return result; - } - - @Override - public void setNum(int num) { - // set the number to NumberPicker - for (int i = 0; i < mIntegralPart + mDecimalPart; i++) { - int digit = (int) Math.pow(10, mIntegralPart + mDecimalPart - (i + 1)); - mNumbers.get(i).setValue(num / digit); - num %= digit; - } - } - - private void changeTo(double target) { - int num = (int) (target * Math.pow(10, mDecimalPart)); - for (int i = 0; i < mIntegralPart + mDecimalPart; i++) { - int digit = (int) Math.pow(10, mIntegralPart + mDecimalPart - (i + 1)); - mNumbers.get(i).setValue(num / digit); - num %= digit; - } - } - - /** - * Change the number of this view to maximum value. - */ - public void changeToMax() { - final double max = mRange.getUpperBound(); - changeTo(max); - } - - /** - * Change the number of this view to minimum value. - */ - public void changeToMin() { - final double min = mRange.getLowerBound(); - changeTo(min); - } } diff --git a/app/src/main/res/drawable-hdpi/icon_while_forever.png b/app/src/main/res/drawable-hdpi/icon_loop.png similarity index 100% rename from app/src/main/res/drawable-hdpi/icon_while_forever.png rename to app/src/main/res/drawable-hdpi/icon_loop.png diff --git a/app/src/main/res/drawable-hdpi/icon_while_num.png b/app/src/main/res/drawable-hdpi/icon_n_times.png similarity index 100% rename from app/src/main/res/drawable-hdpi/icon_while_num.png rename to app/src/main/res/drawable-hdpi/icon_n_times.png diff --git a/app/src/main/res/layout/block_while_forever.xml b/app/src/main/res/layout/block_loop.xml similarity index 96% rename from app/src/main/res/layout/block_while_forever.xml rename to app/src/main/res/layout/block_loop.xml index 805bd20..2cd5072 100644 --- a/app/src/main/res/layout/block_while_forever.xml +++ b/app/src/main/res/layout/block_loop.xml @@ -33,6 +33,6 @@ android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:contentDescription="@string/noImage" - android:src="@drawable/icon_while_forever"/> + android:src="@drawable/icon_loop"/> \ No newline at end of file diff --git a/app/src/main/res/layout/block_while_num.xml b/app/src/main/res/layout/block_n_times.xml similarity index 97% rename from app/src/main/res/layout/block_while_num.xml rename to app/src/main/res/layout/block_n_times.xml index fde8a48..7571e14 100644 --- a/app/src/main/res/layout/block_while_num.xml +++ b/app/src/main/res/layout/block_n_times.xml @@ -47,6 +47,6 @@ android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:contentDescription="@string/noImage" - android:src="@drawable/icon_while_num"/> + android:src="@drawable/icon_n_times"/> \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 83c5c86..33590ed 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -30,6 +30,7 @@ ぶんき くりかえし % + びょう エラー Bluetoothきのうをオンにしました Bluetooth きのうがオンになっていません @@ -62,8 +63,8 @@ - ずっとくりかえす - □かいくりかえす + ずっとくりかえす + □かいくりかえす くりかえしをぬける %d かい diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 84f7938..5cfa1d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,6 +30,7 @@ Selection Repetition % + s Error Turned on Bluetooth Please turn on Bluetooth. @@ -62,14 +63,14 @@ - Repeat forever - Repeat □times + Repeat forever + Repeat □times Exit a loop Repeats: %d - 0.0 + 0 0 Repeat @@ -85,7 +86,7 @@ Execute Screen Connecting... - This device doesn\'t have Bluetooth function. + This device does not have Bluetooth function. Please wait for a while. Error: Failed to connect the device. Execution starts. diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/activity/NxtBlockListActivity.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/activity/NxtBlockListActivity.java index c36ce60..30ac336 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/activity/NxtBlockListActivity.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/activity/NxtBlockListActivity.java @@ -16,9 +16,9 @@ package com.pileproject.drive.programming.visual.activity; +import com.pileproject.drive.programming.visual.block.repetition.LoopBlock; +import com.pileproject.drive.programming.visual.block.repetition.NTimesBlock; import com.pileproject.drive.programming.visual.block.repetition.RepetitionBreakBlock; -import com.pileproject.drive.programming.visual.block.repetition.WhileForeverBlock; -import com.pileproject.drive.programming.visual.block.repetition.WhileNumBlock; import com.pileproject.drive.programming.visual.block.selection.IfNxtIsOutOfLineBlock; import com.pileproject.drive.programming.visual.block.selection.IfNxtWasTouchedBlock; import com.pileproject.drive.programming.visual.block.selection.IfThereWasALargeSoundBlock; @@ -43,8 +43,8 @@ protected BlockClassHolder[][] getBlockIcons() { new BlockClassHolder(SetLeftMotorSpeedBlock.class), new BlockClassHolder(SetRightMotorSpeedBlock.class), }, { - new BlockClassHolder(WhileForeverBlock.class), - new BlockClassHolder(WhileNumBlock.class), + new BlockClassHolder(LoopBlock.class), + new BlockClassHolder(NTimesBlock.class), new BlockClassHolder(RepetitionBreakBlock.class), }, { new BlockClassHolder(IfNxtIsOutOfLineBlock.class), diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfNxtIsOutOfLineBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfNxtIsOutOfLineBlock.java index c5a286a..958564e 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfNxtIsOutOfLineBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfNxtIsOutOfLineBlock.java @@ -17,10 +17,8 @@ package com.pileproject.drive.programming.visual.block.selection; import android.content.Context; -import android.view.LayoutInflater; import com.pileproject.drive.R; -import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; import com.pileproject.drive.preferences.BlockPreferences; @@ -38,17 +36,15 @@ public class IfNxtIsOutOfLineBlock extends SelectionBlock { private int mThreshold; public IfNxtIsOutOfLineBlock(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.block_if_nxt_is_out_of_line, this); + super(context, R.layout.block_if_nxt_is_out_of_line); mThreshold = BlockPreferences.get(context).getLineSensorThreshold(); } @Override - public int action(MachineController controller, ExecutionCondition condition) { + protected boolean evaluateCondition(MachineController controller) { // comment is weird. // getLightPercent returns tenfold value - condition.pushSelectionResult(((NxtController) controller).getLineSensorValue() > mThreshold * 10); - return 0; + return ((NxtController) controller).getLineSensorValue() > mThreshold * 10; } } diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfNxtWasTouchedBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfNxtWasTouchedBlock.java index 11d8ecc..e492d62 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfNxtWasTouchedBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfNxtWasTouchedBlock.java @@ -17,10 +17,8 @@ package com.pileproject.drive.programming.visual.block.selection; import android.content.Context; -import android.view.LayoutInflater; import com.pileproject.drive.R; -import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; @@ -33,13 +31,11 @@ public class IfNxtWasTouchedBlock extends SelectionBlock { public IfNxtWasTouchedBlock(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.block_if_nxt_was_touched, this); + super(context, R.layout.block_if_nxt_was_touched); } @Override - public int action(MachineController controller, ExecutionCondition condition) { - condition.pushSelectionResult(((NxtController) controller).getTouchSensorValue()); - return 0; + protected boolean evaluateCondition(MachineController controller) { + return ((NxtController) controller).getTouchSensorValue(); } } diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfThereWasALargeSoundBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfThereWasALargeSoundBlock.java index 723f988..a2cdbea 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfThereWasALargeSoundBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/selection/IfThereWasALargeSoundBlock.java @@ -17,10 +17,8 @@ package com.pileproject.drive.programming.visual.block.selection; import android.content.Context; -import android.view.LayoutInflater; import com.pileproject.drive.R; -import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; import com.pileproject.drive.preferences.BlockPreferences; @@ -38,17 +36,15 @@ public class IfThereWasALargeSoundBlock extends SelectionBlock { private int mThreshold; public IfThereWasALargeSoundBlock(Context context) { - super(context); - LayoutInflater.from(context).inflate(R.layout.block_if_there_was_a_large_sound, this); + super(context, R.layout.block_if_there_was_a_large_sound); mThreshold = BlockPreferences.get(context).getSoundSensorThreshold(); } @Override - public int action(MachineController controller, ExecutionCondition condition) { + protected boolean evaluateCondition(MachineController controller) { // need multiply 10 because getdB returns 10 times value // the comment is messed up - condition.pushSelectionResult(((NxtController) controller).getSoundSensorValue() > mThreshold * 10); - return 0; + return ((NxtController) controller).getSoundSensorValue() > mThreshold * 10; } } diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/BackwardSecBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/BackwardSecBlock.java index 9dd9aab..8050694 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/BackwardSecBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/BackwardSecBlock.java @@ -17,17 +17,15 @@ package com.pileproject.drive.programming.visual.block.sequence; import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; -import java.util.Locale; +import java.math.BigDecimal; /** * Backward for a while @@ -35,47 +33,32 @@ * @author yusaku * @version 1.0 7-July-2013 */ -public class BackwardSecBlock extends SequenceBlockHasNumText { +public class BackwardSecBlock extends SequenceBlockHasNumberText { - public BackwardSecBlock(Context context) { - super(context); - - View layout = LayoutInflater.from(context).inflate(R.layout.block_backward_sec, this); - numText = (TextView) layout.findViewById(R.id.block_numText); - } - - @Override - public int getNum() { - double raw = Double.parseDouble(numText.getText().toString()); - return (int) (raw * 1000); - } + // TODO: set from preference + private static final Range range = Range.closed(BigDecimal.ZERO, new BigDecimal(3)); - @Override - public void setNum(int num) { - double raw = num / 1000.0; - numText.setText(String.format(Locale.ENGLISH, "%.3f", raw)); - } + // TODO: set from preference + private static final int PRECISION = 3; - @Override - public Integer[] getDigit() { - return new Integer[]{1, 3}; + public BackwardSecBlock(Context context) { + super(context, R.layout.block_backward_sec, R.id.block_numText); } @Override - public double getMax() { - return 3.0; + public int getPrecision() { + return PRECISION; } @Override - public double getMin() { - return 0.0; + public Range getRange() { + return range; } @Override - public int action( - MachineController controller, ExecutionCondition condition) { + public int action(MachineController controller, ExecutionCondition condition) { ((NxtController) controller).moveBackward(); - return getNum(); + return getActionValue(); } @Override diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/ForwardSecBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/ForwardSecBlock.java index 5114997..c390c27 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/ForwardSecBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/ForwardSecBlock.java @@ -17,17 +17,15 @@ package com.pileproject.drive.programming.visual.block.sequence; import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; -import java.util.Locale; +import java.math.BigDecimal; /** * Forward for a while @@ -35,48 +33,32 @@ * @author yusaku * @version 1.0 7-July-2013 */ -public class ForwardSecBlock extends SequenceBlockHasNumText { +public class ForwardSecBlock extends SequenceBlockHasNumberText { - public ForwardSecBlock(Context context) { - super(context); - // Create view defined as the layout XML by LayoutInflater - // Set this view to LayoutInflater#inflate() 2nd argument - View layout = LayoutInflater.from(context).inflate(R.layout.block_forward_sec, this); - numText = (TextView) layout.findViewById(R.id.block_numText); - } + // TODO: set from preference + private static final Range range = Range.closed(BigDecimal.ZERO, new BigDecimal(3)); - @Override - public int getNum() { - double raw = Double.parseDouble(numText.getText().toString()); - return (int) (raw * 1000); - } + // TODO: set from preference + private static final int PRECISION = 3; - @Override - public void setNum(int num) { - double raw = (double) num / 1000.0; - numText.setText(String.format(Locale.ENGLISH, "%.3f", raw)); - } - - @Override - public Integer[] getDigit() { - return new Integer[]{1, 3}; + public ForwardSecBlock(Context context) { + super(context, R.layout.block_forward_sec, R.id.block_numText); } @Override - public double getMax() { - return 3.0; + public int getPrecision() { + return PRECISION; } @Override - public double getMin() { - return 0.000; + public Range getRange() { + return range; } @Override - public int action( - MachineController controller, ExecutionCondition condition) { + public int action(MachineController controller, ExecutionCondition condition) { ((NxtController) controller).moveForward(); - return getNum(); + return getActionValue(); } @Override diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/SetLeftMotorSpeedBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/SetLeftMotorSpeedBlock.java index 76e8db8..8ab5531 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/SetLeftMotorSpeedBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/SetLeftMotorSpeedBlock.java @@ -17,15 +17,15 @@ package com.pileproject.drive.programming.visual.block.sequence; import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; + +import java.math.BigDecimal; /** * Set left motor power @@ -33,43 +33,31 @@ * @author yusaku * @version 1.0 7-July-2013 */ -public class SetLeftMotorSpeedBlock extends SequenceBlockHasNumText { +public class SetLeftMotorSpeedBlock extends SequenceBlockHasNumberText { - public SetLeftMotorSpeedBlock(Context context) { - super(context); - View layout = LayoutInflater.from(context).inflate(R.layout.block_set_left_motor_speed, this); - numText = (TextView) layout.findViewById(R.id.block_numText); - } + // TODO: set from preference + private static final Range range = Range.closed(BigDecimal.ZERO, new BigDecimal(100)); - @Override - public int getNum() { - return Integer.parseInt(numText.getText().toString()); - } + // TODO: set from preference + private static final int PRECISION = 0; - @Override - public void setNum(int num) { - numText.setText(String.valueOf(num)); - } - - @Override - public Integer[] getDigit() { - return new Integer[]{3, 0}; + public SetLeftMotorSpeedBlock(Context context) { + super(context, R.layout.block_set_left_motor_speed, R.id.block_numText); } @Override - public double getMax() { - return 100; + public int getPrecision() { + return PRECISION; } @Override - public double getMin() { - return 0; + public Range getRange() { + return range; } @Override - public int action( - MachineController controller, ExecutionCondition condition) { - ((NxtController) controller).setMotorPower(NxtController.MotorKind.LeftMotor, getNum()); + public int action(MachineController controller, ExecutionCondition condition) { + ((NxtController) controller).setMotorPower(NxtController.MotorKind.LeftMotor, getValue().doubleValue()); return 0; } diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/SetRightMotorSpeedBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/SetRightMotorSpeedBlock.java index d6b7fac..8b69a51 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/SetRightMotorSpeedBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/SetRightMotorSpeedBlock.java @@ -17,15 +17,15 @@ package com.pileproject.drive.programming.visual.block.sequence; import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; + +import java.math.BigDecimal; /** * Set right motor power @@ -33,43 +33,31 @@ * @author yusaku * @version 1.0 7-July-2013 */ -public class SetRightMotorSpeedBlock extends SequenceBlockHasNumText { +public class SetRightMotorSpeedBlock extends SequenceBlockHasNumberText { - public SetRightMotorSpeedBlock(Context context) { - super(context); - View layout = LayoutInflater.from(context).inflate(R.layout.block_set_right_motor_speed, this); - numText = (TextView) layout.findViewById(R.id.block_numText); - } - - @Override - public int getNum() { - return Integer.parseInt(numText.getText().toString()); - } + // TODO: set from preference + private static final Range range = Range.closed(BigDecimal.ZERO, new BigDecimal(100)); - @Override - public void setNum(int num) { - numText.setText(String.valueOf(num)); - } + // TODO: set from preference + private static final int PRECISION = 0; - @Override - public Integer[] getDigit() { - return new Integer[]{3, 0}; + public SetRightMotorSpeedBlock(Context context) { + super(context, R.layout.block_set_right_motor_speed, R.id.block_numText); } @Override - public double getMax() { - return 100.0; + public int getPrecision() { + return PRECISION; } @Override - public double getMin() { - return 0.0; + public Range getRange() { + return range; } @Override - public int action( - MachineController controller, ExecutionCondition condition) { - ((NxtController) controller).setMotorPower(NxtController.MotorKind.RightMotor, getNum()); + public int action(MachineController controller, ExecutionCondition condition) { + ((NxtController) controller).setMotorPower(NxtController.MotorKind.RightMotor, getValue().doubleValue()); return 0; } diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/StopSecBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/StopSecBlock.java index d8ae14a..97b44f6 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/StopSecBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/StopSecBlock.java @@ -17,16 +17,14 @@ package com.pileproject.drive.programming.visual.block.sequence; import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; -import java.util.Locale; +import java.math.BigDecimal; /** * Stop for a while @@ -34,46 +32,32 @@ * @author yusaku * @version 1.0 7-July-2013 */ -public class StopSecBlock extends SequenceBlockHasNumText { +public class StopSecBlock extends SequenceBlockHasNumberText { - public StopSecBlock(Context context) { - super(context); - View layout = LayoutInflater.from(context).inflate(R.layout.block_stop_sec, this); - numText = (TextView) layout.findViewById(R.id.block_numText); - } + // TODO: set from preference + private static final Range range = Range.closed(BigDecimal.ZERO, new BigDecimal(3)); - @Override - public int getNum() { - double raw = Double.parseDouble(numText.getText().toString()); - return (int) (raw * 1000.0); - } + // TODO: set from preference + private static final int PRECISION = 3; - @Override - public void setNum(int num) { - double raw = num / 1000.0; - numText.setText(String.format(Locale.ENGLISH, "%.3f", raw)); - } - - @Override - public Integer[] getDigit() { - return new Integer[]{1, 3}; + public StopSecBlock(Context context) { + super(context, R.layout.block_stop_sec, R.id.block_numText); } @Override - public double getMax() { - return 3.0; + public int getPrecision() { + return PRECISION; } @Override - public double getMin() { - return 0.0; + public Range getRange() { + return range; } @Override - public int action( - MachineController controller, ExecutionCondition condition) { + public int action(MachineController controller, ExecutionCondition condition) { controller.halt(); - return getNum(); + return getActionValue(); } @Override diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/TurnLeftSecBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/TurnLeftSecBlock.java index 98945fc..59336c3 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/TurnLeftSecBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/TurnLeftSecBlock.java @@ -17,17 +17,15 @@ package com.pileproject.drive.programming.visual.block.sequence; import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; -import java.util.Locale; +import java.math.BigDecimal; /** * Turn left for a while @@ -35,46 +33,32 @@ * @author yusaku * @version 1.0 7-July-2013 */ -public class TurnLeftSecBlock extends SequenceBlockHasNumText { +public class TurnLeftSecBlock extends SequenceBlockHasNumberText { - public TurnLeftSecBlock(Context context) { - super(context); - View layout = LayoutInflater.from(context).inflate(R.layout.block_turn_left_sec, this); - numText = (TextView) layout.findViewById(R.id.block_numText); - } + // TODO: set from preference + private static final Range range = Range.closed(BigDecimal.ZERO, new BigDecimal(3)); - @Override - public int getNum() { - double raw = Double.parseDouble(numText.getText().toString()); - return (int) (raw * 1000); - } + // TODO: set from preference + private static final int PRECISION = 3; - @Override - public void setNum(int num) { - double raw = num / 1000.0; - numText.setText(String.format(Locale.ENGLISH, "%.3f", raw)); - } - - @Override - public Integer[] getDigit() { - return new Integer[]{1, 3}; + public TurnLeftSecBlock(Context context) { + super(context, R.layout.block_turn_left_sec, R.id.block_numText); } @Override - public double getMax() { - return 3.0; + public int getPrecision() { + return PRECISION; } @Override - public double getMin() { - return 0.0; + public Range getRange() { + return range; } @Override - public int action( - MachineController controller, ExecutionCondition condition) { + public int action(MachineController controller, ExecutionCondition condition) { ((NxtController) controller).turnLeft(); - return getNum(); + return getActionValue(); } @Override diff --git a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/TurnRightSecBlock.java b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/TurnRightSecBlock.java index 61f020f..91ad4f0 100644 --- a/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/TurnRightSecBlock.java +++ b/app/src/nxt/java/com/pileproject/drive/programming/visual/block/sequence/TurnRightSecBlock.java @@ -17,17 +17,15 @@ package com.pileproject.drive.programming.visual.block.sequence; import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; import com.pileproject.drive.R; import com.pileproject.drive.execution.ExecutionCondition; import com.pileproject.drive.execution.MachineController; import com.pileproject.drive.execution.NxtController; import com.pileproject.drive.util.development.Unit; +import com.pileproject.drive.util.math.Range; -import java.util.Locale; +import java.math.BigDecimal; /** * Turn right for a while @@ -35,46 +33,32 @@ * @author yusaku * @version 1.0 7-July-2013 */ -public class TurnRightSecBlock extends SequenceBlockHasNumText { +public class TurnRightSecBlock extends SequenceBlockHasNumberText { - public TurnRightSecBlock(Context context) { - super(context); - View layout = LayoutInflater.from(context).inflate(R.layout.block_turn_right_sec, this); - numText = (TextView) layout.findViewById(R.id.block_numText); - } + // TODO: set from preference + private static final Range range = Range.closed(BigDecimal.ZERO, new BigDecimal(3)); - @Override - public int getNum() { - double raw = Double.parseDouble(numText.getText().toString()); - return (int) (raw * 1000); - } + // TODO: set from preference + private static final int PRECISION = 3; - @Override - public void setNum(int num) { - double raw = num / 1000.0; - numText.setText(String.format(Locale.ENGLISH, "%.3f", raw)); - } - - @Override - public Integer[] getDigit() { - return new Integer[]{1, 3}; + public TurnRightSecBlock(Context context) { + super(context, R.layout.block_turn_right_sec, R.id.block_numText); } @Override - public double getMax() { - return 3.0; + public int getPrecision() { + return PRECISION; } @Override - public double getMin() { - return 0.0; + public Range getRange() { + return range; } @Override - public int action( - MachineController controller, ExecutionCondition condition) { + public int action(MachineController controller, ExecutionCondition condition) { ((NxtController) controller).turnRight(); - return getNum(); + return getActionValue(); } @Override diff --git a/app/src/test/java/com/pileproject/drive/util/string/NumberUtilTest.java b/app/src/test/java/com/pileproject/drive/util/string/NumberUtilTest.java new file mode 100644 index 0000000..f71bc68 --- /dev/null +++ b/app/src/test/java/com/pileproject/drive/util/string/NumberUtilTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.util.string; + +import org.junit.Test; + +import java.math.BigDecimal; +import java.util.Locale; + +import static org.junit.Assert.*; + +public class NumberUtilTest { + + @Test + public void whenLocaleIsJapan_thenConvertsAsJapaneseFormat() throws Exception { + assertEquals("When the precision 3 and locale Japan", + "1.250", NumberUtil.toString(Locale.JAPAN, 1.25, 3)); + } + + @Test + public void whenLocaleIsFrance_thenConvertsAsFrenchFormat() throws Exception { + assertEquals("When the precision 3 and locale France", + "1,250", NumberUtil.toString(Locale.FRANCE, 1.25, 3)); + } + + @Test + public void whenValueTypeIsBigDecimal_thenConvertsSuccessfully() throws Exception { + assertEquals("When the precision 4 and locale Japan", + "1.2345", NumberUtil.toString(Locale.JAPAN, new BigDecimal("1.2345"), 4)); + } +} \ No newline at end of file diff --git a/app/src/test/java/com/pileproject/drive/util/string/ParseUtilTest.java b/app/src/test/java/com/pileproject/drive/util/string/ParseUtilTest.java new file mode 100644 index 0000000..33d4c37 --- /dev/null +++ b/app/src/test/java/com/pileproject/drive/util/string/ParseUtilTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011-2015 PILE Project, 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.pileproject.drive.util.string; + +import org.junit.Test; + +import java.math.BigDecimal; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; + +public class ParseUtilTest { + + @Test + public void testDoubleValueOf_whenLocaleIsJapan_thenRegardsDotAsRadixPoint() throws Exception { + + // there's no error when a double value represents 1.250 so let delta be 0.0 + assertEquals(1.250, ParseUtil.doubleValueOf("1.250", Locale.JAPAN), 0.0); + } + + @Test + public void testDoubleValueOf_whenLocaleIsEnglish_thenRegardsDotAsRadixPoint() throws Exception { + + assertEquals(1.250, ParseUtil.doubleValueOf("1.250", Locale.ENGLISH), 0.0); + } + + @Test + public void testDoubleValueOf_whenLocaleIsFrance_thenRegardsCommaAsRadixPoint() throws Exception { + + assertEquals(1.250, ParseUtil.doubleValueOf("1,250", Locale.FRANCE), 0.0); + } + + @Test + public void testDoubleValueOf_whenLocaleIsJapan_thenRegardsCommaAsSeparator() throws Exception { + + assertEquals(1_250, ParseUtil.doubleValueOf("1,250", Locale.JAPAN), 0.0); + } + + @Test(expected = NumberFormatException.class) + public void testDoubleValueOf_whenStringIsNotNumber_thenThrowsException() throws Exception { + ParseUtil.doubleValueOf("foobar"); + } + + @Test + public void testBigDecimalValueOf_whenLocaleIsJapan_thenRegardsDotAsRadixPoint() throws Exception { + + assertEquals(new BigDecimal("1.234"), ParseUtil.bigDecimalValueOf("1.234", Locale.JAPAN)); + } + + @Test + public void testBigDecimalValueOf_whenLocaleIsEnglish_thenRegardsDotAsRadixPoint() throws Exception { + + assertEquals(new BigDecimal("1.234"), ParseUtil.bigDecimalValueOf("1.234", Locale.ENGLISH)); + } + + @Test + public void testBigDecimalValueOf_whenLocaleIsFrance_thenRegardsCommaAsRadixPoint() throws Exception { + + assertEquals(new BigDecimal("1.234"), ParseUtil.bigDecimalValueOf("1,234", Locale.FRANCE)); + } + + @Test + public void testBigDecimalValueOf_whenLocaleIsJapan_thenRegardsCommaAsSeparator() throws Exception { + + assertEquals(new BigDecimal(1_234), ParseUtil.bigDecimalValueOf("1,234", Locale.JAPAN)); + } + + @Test(expected = NumberFormatException.class) + public void testBigDecimalValueOf_whenStringIsNotNumber_thenThrowsException() throws Exception { + ParseUtil.bigDecimalValueOf("foobar"); + } +} \ No newline at end of file