Plotter
@ -85,7 +85,7 @@ dependencies {
|
|||||||
exclude(module: 'xercesImpl')
|
exclude(module: 'xercesImpl')
|
||||||
}
|
}
|
||||||
compile 'org.solovyev.android:checkout:0.7.2@aar'
|
compile 'org.solovyev.android:checkout:0.7.2@aar'
|
||||||
compile 'org.solovyev.android:material:0.1.3@aar'
|
compile 'org.solovyev.android:material:0.1.4@aar'
|
||||||
compile 'com.google.android.gms:play-services-ads:8.4.0'
|
compile 'com.google.android.gms:play-services-ads:8.4.0'
|
||||||
compile 'com.google.android.gms:play-services-base:8.4.0'
|
compile 'com.google.android.gms:play-services-base:8.4.0'
|
||||||
compile 'com.google.android.gms:play-services-analytics:8.4.0'
|
compile 'com.google.android.gms:play-services-analytics:8.4.0'
|
||||||
|
@ -117,6 +117,10 @@
|
|||||||
android:label="@string/c_vars_and_constants"
|
android:label="@string/c_vars_and_constants"
|
||||||
android:theme="@style/Cpp.Theme.Dialog" />
|
android:theme="@style/Cpp.Theme.Dialog" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".plot.PlotActivity"
|
||||||
|
android:label="@string/cpp_plotter" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".wizard.WizardActivity"
|
android:name=".wizard.WizardActivity"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
|
@ -39,6 +39,7 @@ import org.solovyev.android.calculator.history.HistoryActivity;
|
|||||||
import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity;
|
import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity;
|
||||||
import org.solovyev.android.calculator.operators.OperatorsActivity;
|
import org.solovyev.android.calculator.operators.OperatorsActivity;
|
||||||
import org.solovyev.android.calculator.plot.CalculatorPlotter;
|
import org.solovyev.android.calculator.plot.CalculatorPlotter;
|
||||||
|
import org.solovyev.android.calculator.plot.PlotActivity;
|
||||||
import org.solovyev.android.calculator.preferences.PreferencesActivity;
|
import org.solovyev.android.calculator.preferences.PreferencesActivity;
|
||||||
import org.solovyev.android.calculator.variables.CppVariable;
|
import org.solovyev.android.calculator.variables.CppVariable;
|
||||||
import org.solovyev.android.calculator.variables.EditVariableFragment;
|
import org.solovyev.android.calculator.variables.EditVariableFragment;
|
||||||
@ -159,7 +160,8 @@ public final class ActivityLauncher implements CalculatorEventListener {
|
|||||||
|
|
||||||
public void showWidgetSettings() {
|
public void showWidgetSettings() {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
show(context, PreferencesActivity.makeIntent(context, R.xml.preferences_widget, R.string.prefs_widget_title));
|
show(context, PreferencesActivity.makeIntent(context, R.xml.preferences_widget,
|
||||||
|
R.string.prefs_widget_title));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showOperators() {
|
public void showOperators() {
|
||||||
@ -170,6 +172,10 @@ public final class ActivityLauncher implements CalculatorEventListener {
|
|||||||
show(getContext(), AboutActivity.getClass(getContext()));
|
show(getContext(), AboutActivity.getClass(getContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showPlotter() {
|
||||||
|
show(getContext(), PlotActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
public void openFacebook() {
|
public void openFacebook() {
|
||||||
final Uri uri = Uri.parse(application.getString(R.string.cpp_share_link));
|
final Uri uri = Uri.parse(application.getString(R.string.cpp_share_link));
|
||||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
|
@ -4,14 +4,18 @@ import org.solovyev.android.calculator.converter.ConverterFragment;
|
|||||||
import org.solovyev.android.calculator.errors.FixableErrorFragment;
|
import org.solovyev.android.calculator.errors.FixableErrorFragment;
|
||||||
import org.solovyev.android.calculator.errors.FixableErrorsActivity;
|
import org.solovyev.android.calculator.errors.FixableErrorsActivity;
|
||||||
import org.solovyev.android.calculator.floating.FloatingCalculatorService;
|
import org.solovyev.android.calculator.floating.FloatingCalculatorService;
|
||||||
import org.solovyev.android.calculator.functions.EditFunctionFragment;
|
import org.solovyev.android.calculator.floating.FloatingCalculatorView;
|
||||||
|
import org.solovyev.android.calculator.functions.BaseFunctionFragment;
|
||||||
import org.solovyev.android.calculator.functions.FunctionsFragment;
|
import org.solovyev.android.calculator.functions.FunctionsFragment;
|
||||||
import org.solovyev.android.calculator.history.BaseHistoryFragment;
|
import org.solovyev.android.calculator.history.BaseHistoryFragment;
|
||||||
import org.solovyev.android.calculator.history.EditHistoryFragment;
|
import org.solovyev.android.calculator.history.EditHistoryFragment;
|
||||||
import org.solovyev.android.calculator.history.HistoryActivity;
|
import org.solovyev.android.calculator.history.HistoryActivity;
|
||||||
import org.solovyev.android.calculator.keyboard.BaseKeyboardUi;
|
import org.solovyev.android.calculator.keyboard.BaseKeyboardUi;
|
||||||
import org.solovyev.android.calculator.floating.FloatingCalculatorView;
|
|
||||||
import org.solovyev.android.calculator.operators.OperatorsFragment;
|
import org.solovyev.android.calculator.operators.OperatorsFragment;
|
||||||
|
import org.solovyev.android.calculator.plot.PlotActivity;
|
||||||
|
import org.solovyev.android.calculator.plot.PlotDimensionsFragment;
|
||||||
|
import org.solovyev.android.calculator.plot.PlotEditFunctionFragment;
|
||||||
|
import org.solovyev.android.calculator.plot.PlotFunctionsFragment;
|
||||||
import org.solovyev.android.calculator.preferences.PreferencesActivity;
|
import org.solovyev.android.calculator.preferences.PreferencesActivity;
|
||||||
import org.solovyev.android.calculator.preferences.PurchaseDialogActivity;
|
import org.solovyev.android.calculator.preferences.PurchaseDialogActivity;
|
||||||
import org.solovyev.android.calculator.variables.EditVariableFragment;
|
import org.solovyev.android.calculator.variables.EditVariableFragment;
|
||||||
@ -32,8 +36,10 @@ public interface AppComponent {
|
|||||||
void inject(FloatingCalculatorService service);
|
void inject(FloatingCalculatorService service);
|
||||||
void inject(BaseHistoryFragment fragment);
|
void inject(BaseHistoryFragment fragment);
|
||||||
void inject(BaseDialogFragment fragment);
|
void inject(BaseDialogFragment fragment);
|
||||||
|
void inject(PlotFunctionsFragment fragment);
|
||||||
void inject(FixableErrorFragment fragment);
|
void inject(FixableErrorFragment fragment);
|
||||||
void inject(EditFunctionFragment fragment);
|
void inject(BaseFunctionFragment fragment);
|
||||||
|
void inject(PlotEditFunctionFragment fragment);
|
||||||
void inject(EditVariableFragment fragment);
|
void inject(EditVariableFragment fragment);
|
||||||
void inject(EditHistoryFragment fragment);
|
void inject(EditHistoryFragment fragment);
|
||||||
void inject(FunctionsFragment fragment);
|
void inject(FunctionsFragment fragment);
|
||||||
@ -51,6 +57,8 @@ public interface AppComponent {
|
|||||||
void inject(FloatingCalculatorView view);
|
void inject(FloatingCalculatorView view);
|
||||||
void inject(DragButtonWizardStep fragment);
|
void inject(DragButtonWizardStep fragment);
|
||||||
void inject(BaseFragment fragment);
|
void inject(BaseFragment fragment);
|
||||||
|
void inject(PlotActivity.MyFragment fragment);
|
||||||
|
void inject(PlotDimensionsFragment fragment);
|
||||||
void inject(HistoryActivity activity);
|
void inject(HistoryActivity activity);
|
||||||
void inject(Tabs tabs);
|
void inject(Tabs tabs);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ import org.solovyev.android.checkout.ProductTypes;
|
|||||||
import org.solovyev.android.checkout.Products;
|
import org.solovyev.android.checkout.Products;
|
||||||
import org.solovyev.android.checkout.RobotmediaDatabase;
|
import org.solovyev.android.checkout.RobotmediaDatabase;
|
||||||
import org.solovyev.android.checkout.RobotmediaInventory;
|
import org.solovyev.android.checkout.RobotmediaInventory;
|
||||||
|
import org.solovyev.android.plotter.Plot;
|
||||||
|
import org.solovyev.android.plotter.Plotter;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@ -192,6 +194,12 @@ public class AppModule {
|
|||||||
return filesDir;
|
return filesDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
Plotter providePlotter() {
|
||||||
|
return Plot.newPlotter(application);
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private File makeFilesDir() {
|
private File makeFilesDir() {
|
||||||
final File filesDir = application.getFilesDir();
|
final File filesDir = application.getFilesDir();
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
package org.solovyev.android.calculator;
|
package org.solovyev.android.calculator;
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.LayoutRes;
|
|
||||||
import android.support.annotation.StringRes;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.view.*;
|
|
||||||
import org.solovyev.android.calculator.ads.AdUi;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static android.view.Menu.NONE;
|
import static android.view.Menu.NONE;
|
||||||
import static org.solovyev.android.calculator.App.cast;
|
import static org.solovyev.android.calculator.App.cast;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.support.annotation.LayoutRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.StringRes;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.ads.AdUi;
|
||||||
|
import org.solovyev.android.plotter.Check;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
public abstract class BaseFragment extends Fragment {
|
public abstract class BaseFragment extends Fragment {
|
||||||
|
|
||||||
private final int layout;
|
private final int layout;
|
||||||
@ -24,10 +32,19 @@ public abstract class BaseFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static MenuItem addMenu(@Nonnull ContextMenu menu, @StringRes int label, @Nonnull MenuItem.OnMenuItemClickListener listener) {
|
public static MenuItem addMenu(@Nonnull ContextMenu menu, @StringRes int label,
|
||||||
|
@Nonnull MenuItem.OnMenuItemClickListener listener) {
|
||||||
return menu.add(NONE, label, NONE, label).setOnMenuItemClickListener(listener);
|
return menu.add(NONE, label, NONE, label).setOnMenuItemClickListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static <P extends Parcelable> P getParcelable(@NonNull Bundle bundle,
|
||||||
|
@NonNull String key) {
|
||||||
|
final P parcelable = bundle.getParcelable(key);
|
||||||
|
Check.isNotNull(parcelable);
|
||||||
|
return parcelable;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -40,7 +57,8 @@ public abstract class BaseFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
final View view = inflater.inflate(layout, container, false);
|
final View view = inflater.inflate(layout, container, false);
|
||||||
adUi.onCreateView(view);
|
adUi.onCreateView(view);
|
||||||
return view;
|
return view;
|
||||||
|
@ -22,6 +22,15 @@
|
|||||||
|
|
||||||
package org.solovyev.android.calculator;
|
package org.solovyev.android.calculator;
|
||||||
|
|
||||||
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||||
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||||
|
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
||||||
|
import static org.solovyev.android.calculator.Preferences.Gui.preventScreenFromFading;
|
||||||
|
import static org.solovyev.android.calculator.release.ReleaseNotes.hasReleaseNotes;
|
||||||
|
import static org.solovyev.android.wizard.WizardUi.continueWizard;
|
||||||
|
import static org.solovyev.android.wizard.WizardUi.createLaunchIntent;
|
||||||
|
import static org.solovyev.android.wizard.WizardUi.startWizard;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
@ -33,11 +42,15 @@ import android.support.v7.app.AlertDialog;
|
|||||||
import android.support.v7.widget.CardView;
|
import android.support.v7.widget.CardView;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.view.*;
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import butterknife.Bind;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import org.solovyev.android.Activities;
|
import org.solovyev.android.Activities;
|
||||||
import org.solovyev.android.Android;
|
import org.solovyev.android.Android;
|
||||||
import org.solovyev.android.calculator.converter.ConverterFragment;
|
import org.solovyev.android.calculator.converter.ConverterFragment;
|
||||||
@ -49,17 +62,13 @@ import org.solovyev.android.wizard.Wizard;
|
|||||||
import org.solovyev.android.wizard.Wizards;
|
import org.solovyev.android.wizard.Wizards;
|
||||||
import org.solovyev.common.Objects;
|
import org.solovyev.common.Objects;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
|
||||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
|
||||||
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
|
||||||
import static org.solovyev.android.calculator.Preferences.Gui.preventScreenFromFading;
|
|
||||||
import static org.solovyev.android.calculator.release.ReleaseNotes.hasReleaseNotes;
|
|
||||||
import static org.solovyev.android.wizard.WizardUi.*;
|
|
||||||
|
|
||||||
public class CalculatorActivity extends BaseActivity implements SharedPreferences.OnSharedPreferenceChangeListener, Toolbar.OnMenuItemClickListener {
|
public class CalculatorActivity extends BaseActivity implements SharedPreferences.OnSharedPreferenceChangeListener, Toolbar.OnMenuItemClickListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -278,7 +287,7 @@ public class CalculatorActivity extends BaseActivity implements SharedPreference
|
|||||||
launcher.showHistory();
|
launcher.showHistory();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_plotter:
|
case R.id.menu_plotter:
|
||||||
Locator.getInstance().getPlotter().plot();
|
launcher.showPlotter();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_conversion_tool:
|
case R.id.menu_conversion_tool:
|
||||||
ConverterFragment.show(this);
|
ConverterFragment.show(this);
|
||||||
|
@ -0,0 +1,546 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013 serso aka se.solovyev
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
* Contact details
|
||||||
|
*
|
||||||
|
* Email: se.solovyev@gmail.com
|
||||||
|
* Site: http://se.solovyev.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.solovyev.android.calculator.functions;
|
||||||
|
|
||||||
|
import static org.solovyev.android.calculator.functions.CppFunction.NO_ID;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.LayoutRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.design.widget.TextInputLayout;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewParent;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.AppComponent;
|
||||||
|
import org.solovyev.android.calculator.BaseDialogFragment;
|
||||||
|
import org.solovyev.android.calculator.Calculator;
|
||||||
|
import org.solovyev.android.calculator.Engine;
|
||||||
|
import org.solovyev.android.calculator.FloatingCalculatorKeyboard;
|
||||||
|
import org.solovyev.android.calculator.Keyboard;
|
||||||
|
import org.solovyev.android.calculator.ParseException;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
import org.solovyev.android.calculator.VariablesRegistry;
|
||||||
|
import org.solovyev.android.calculator.keyboard.FloatingKeyboardWindow;
|
||||||
|
import org.solovyev.android.calculator.view.EditTextCompat;
|
||||||
|
import org.solovyev.common.math.MathRegistry;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public abstract class BaseFunctionFragment extends BaseDialogFragment implements View.OnClickListener, View.OnFocusChangeListener, View.OnKeyListener {
|
||||||
|
|
||||||
|
protected static final String ARG_FUNCTION = "function";
|
||||||
|
private static final int MENU_FUNCTION = Menu.FIRST;
|
||||||
|
private static final int MENU_CONSTANT = Menu.FIRST + 1;
|
||||||
|
private static final int MENU_CATEGORY = Menu.FIRST + 2;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final FloatingKeyboardWindow keyboardWindow = new FloatingKeyboardWindow(null);
|
||||||
|
@NonNull
|
||||||
|
private final KeyboardUser keyboardUser = new KeyboardUser();
|
||||||
|
@Bind(R.id.function_params)
|
||||||
|
FunctionParamsView paramsView;
|
||||||
|
@Bind(R.id.function_name_label)
|
||||||
|
TextInputLayout nameLabel;
|
||||||
|
@Bind(R.id.function_name)
|
||||||
|
public EditText nameView;
|
||||||
|
@Bind(R.id.function_body_label)
|
||||||
|
public TextInputLayout bodyLabel;
|
||||||
|
@Bind(R.id.function_body)
|
||||||
|
public EditTextCompat bodyView;
|
||||||
|
@Bind(R.id.function_description)
|
||||||
|
EditText descriptionView;
|
||||||
|
@Inject
|
||||||
|
Calculator calculator;
|
||||||
|
@Inject
|
||||||
|
Keyboard keyboard;
|
||||||
|
@Inject
|
||||||
|
FunctionsRegistry functionsRegistry;
|
||||||
|
@Inject
|
||||||
|
VariablesRegistry variablesRegistry;
|
||||||
|
@Nullable
|
||||||
|
protected CppFunction function;
|
||||||
|
@LayoutRes
|
||||||
|
private final int layout;
|
||||||
|
|
||||||
|
protected BaseFunctionFragment(@LayoutRes int layout) {
|
||||||
|
this.layout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
if (arguments != null) {
|
||||||
|
function = arguments.getParcelable(ARG_FUNCTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(@NonNull AppComponent component) {
|
||||||
|
super.inject(component);
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPrepareDialog(@NonNull AlertDialog.Builder builder) {
|
||||||
|
builder.setNegativeButton(R.string.c_cancel, null);
|
||||||
|
builder.setPositiveButton(R.string.ok, null);
|
||||||
|
builder.setTitle(isNewFunction() ? R.string.function_create_function :
|
||||||
|
R.string.function_edit_function);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean isNewFunction() {
|
||||||
|
return function == null || function.id == NO_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AlertDialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final AlertDialog dialog = super.onCreateDialog(savedInstanceState);
|
||||||
|
dialog.setCanceledOnTouchOutside(false);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShowDialog(@NonNull AlertDialog dialog, boolean firstTime) {
|
||||||
|
if (firstTime) {
|
||||||
|
nameView.selectAll();
|
||||||
|
showIme(nameView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
|
if (v instanceof EditText && FunctionParamsView.PARAM_VIEW_TAG.equals(v.getTag())) {
|
||||||
|
final ViewParent parentView = v.getParent();
|
||||||
|
if (parentView instanceof TextInputLayout) {
|
||||||
|
if (hasFocus) {
|
||||||
|
clearError((TextInputLayout) parentView);
|
||||||
|
} else {
|
||||||
|
validateParameters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int id = v.getId();
|
||||||
|
switch (id) {
|
||||||
|
case R.id.function_name:
|
||||||
|
if (hasFocus) {
|
||||||
|
clearError(nameLabel);
|
||||||
|
} else {
|
||||||
|
validateName();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R.id.function_body:
|
||||||
|
if (hasFocus) {
|
||||||
|
clearError(bodyLabel);
|
||||||
|
showKeyboard();
|
||||||
|
} else {
|
||||||
|
keyboardWindow.hide();
|
||||||
|
validateBody();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showKeyboard() {
|
||||||
|
keyboardWindow.show(new FloatingCalculatorKeyboard(keyboardUser, collectParameters()),
|
||||||
|
getDialog());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected final List<String> collectParameters() {
|
||||||
|
final List<String> parameters = new ArrayList<>();
|
||||||
|
for (String parameter : paramsView.getParams()) {
|
||||||
|
if (!TextUtils.isEmpty(parameter)) {
|
||||||
|
parameters.add(parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
switch (v.getId()) {
|
||||||
|
case R.id.function_body:
|
||||||
|
showKeyboard();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.onClick(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
switch (which) {
|
||||||
|
case DialogInterface.BUTTON_POSITIVE:
|
||||||
|
tryClose();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.onClick(dialog, which);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||||
|
if (v.getId() == R.id.function_body) {
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK && keyboardWindow.isShown()) {
|
||||||
|
keyboardWindow.hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void tryClose() {
|
||||||
|
if (!validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final CppFunction function = collectData();
|
||||||
|
if (function == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (applyData(function)) {
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private CppFunction collectData() {
|
||||||
|
try {
|
||||||
|
final String body = calculator.prepare(bodyView.getText().toString()).getValue();
|
||||||
|
|
||||||
|
return CppFunction.builder(nameView.getText().toString(), body)
|
||||||
|
.withId(isNewFunction() ? NO_ID : function.id)
|
||||||
|
.withParameters(collectParameters())
|
||||||
|
.withDescription(descriptionView.getText().toString()).build();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
setError(bodyLabel, e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validate() {
|
||||||
|
return validateName() & validateParameters() & validateBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean validateName() {
|
||||||
|
final String name = nameView.getText().toString();
|
||||||
|
if (!Engine.isValidName(name)) {
|
||||||
|
setError(nameLabel, getString(R.string.function_name_is_not_valid));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
clearError(nameLabel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateBody() {
|
||||||
|
final String body = bodyView.getText().toString();
|
||||||
|
if (TextUtils.isEmpty(body)) {
|
||||||
|
setError(bodyLabel, getString(R.string.function_is_empty));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
calculator.prepare(body);
|
||||||
|
clearError(bodyLabel);
|
||||||
|
return true;
|
||||||
|
} catch (ParseException e) {
|
||||||
|
setError(bodyLabel, e.getLocalizedMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateParameters() {
|
||||||
|
boolean valid = true;
|
||||||
|
final List<String> parameters = paramsView.getParams();
|
||||||
|
final Set<String> usedParameters = new HashSet<>();
|
||||||
|
for (int i = 0; i < parameters.size(); i++) {
|
||||||
|
final String parameter = parameters.get(i);
|
||||||
|
final TextInputLayout paramLabel = paramsView.getParamLabel(i);
|
||||||
|
if (TextUtils.isEmpty(parameter)) {
|
||||||
|
clearError(paramLabel);
|
||||||
|
} else if (!Engine.isValidName(parameter)) {
|
||||||
|
valid = false;
|
||||||
|
setError(paramLabel, getString(R.string.invalid_name));
|
||||||
|
} else if (usedParameters.contains(parameter)) {
|
||||||
|
valid = false;
|
||||||
|
setError(paramLabel, getString(R.string.function_duplicate_parameter));
|
||||||
|
} else {
|
||||||
|
usedParameters.add(parameter);
|
||||||
|
clearError(paramLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InflateParams")
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected View onCreateDialogView(@NonNull Context context, @NonNull LayoutInflater inflater, @Nullable Bundle savedInstanceState) {
|
||||||
|
final View view = inflater.inflate(layout, null);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
|
if (savedInstanceState == null && function != null) {
|
||||||
|
paramsView.addParams(function.getParameters());
|
||||||
|
nameView.setText(function.getName());
|
||||||
|
descriptionView.setText(function.getDescription());
|
||||||
|
bodyView.setText(function.getBody());
|
||||||
|
}
|
||||||
|
nameView.setOnFocusChangeListener(this);
|
||||||
|
paramsView.setOnFocusChangeListener(this);
|
||||||
|
bodyView.setOnClickListener(this);
|
||||||
|
bodyView.setOnFocusChangeListener(this);
|
||||||
|
bodyView.setOnKeyListener(this);
|
||||||
|
bodyView.dontShowSoftInputOnFocusCompat();
|
||||||
|
descriptionView.setOnFocusChangeListener(this);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KeyboardUser implements FloatingCalculatorKeyboard.User, MenuItem.OnMenuItemClickListener {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Context getContext() {
|
||||||
|
return getActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Resources getResources() {
|
||||||
|
return BaseFunctionFragment.this.getResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public EditText getEditor() {
|
||||||
|
return bodyView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewGroup getKeyboard() {
|
||||||
|
return keyboardWindow.getContentView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertOperator(char operator) {
|
||||||
|
insertOperator(String.valueOf(operator));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int clampSelection(int selection) {
|
||||||
|
return selection < 0 ? 0 : selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertOperator(@NonNull String operator) {
|
||||||
|
final int start = clampSelection(bodyView.getSelectionStart());
|
||||||
|
final int end = clampSelection(bodyView.getSelectionEnd());
|
||||||
|
final Editable e = bodyView.getText();
|
||||||
|
e.replace(start, end, getOperator(start, end, e, operator));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String getOperator(int start, int end, @NonNull Editable e, @NonNull CharSequence operator) {
|
||||||
|
boolean spaceBefore = true;
|
||||||
|
boolean spaceAfter = true;
|
||||||
|
if (start > 0 && Character.isSpaceChar(e.charAt(start - 1))) {
|
||||||
|
spaceBefore = false;
|
||||||
|
}
|
||||||
|
if (end < e.length() && Character.isSpaceChar(e.charAt(end))) {
|
||||||
|
spaceAfter = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spaceBefore && spaceAfter) {
|
||||||
|
return " " + operator + " ";
|
||||||
|
}
|
||||||
|
if (spaceBefore) {
|
||||||
|
return " " + operator;
|
||||||
|
}
|
||||||
|
if (spaceAfter) {
|
||||||
|
return operator + " ";
|
||||||
|
}
|
||||||
|
return String.valueOf(operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showConstants(@NonNull View v) {
|
||||||
|
bodyView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
|
final int id = v.getId();
|
||||||
|
if (id == R.id.function_body) {
|
||||||
|
menu.clear();
|
||||||
|
addEntities(menu, getNamesSorted(variablesRegistry), MENU_CONSTANT);
|
||||||
|
unregisterForContextMenu(bodyView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bodyView.showContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private List<String> getNamesSorted(@NonNull MathRegistry<?> registry) {
|
||||||
|
final List<String> names = new ArrayList<>(registry.getNames());
|
||||||
|
Collections.sort(names);
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showFunctions(@NonNull View v) {
|
||||||
|
bodyView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
|
final int id = v.getId();
|
||||||
|
if (id == R.id.function_body) {
|
||||||
|
menu.clear();
|
||||||
|
addEntities(menu, getNamesSorted(functionsRegistry), MENU_FUNCTION);
|
||||||
|
unregisterForContextMenu(bodyView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bodyView.showContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEntities(@NonNull Menu menu, @NonNull List<String> entities, int groupId) {
|
||||||
|
for (String entity : entities) {
|
||||||
|
menu.add(groupId, Menu.NONE, Menu.NONE, entity).setOnMenuItemClickListener(KeyboardUser.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showFunctionsConstants(@NonNull View v) {
|
||||||
|
bodyView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
|
final int id = v.getId();
|
||||||
|
if (id == R.id.function_body) {
|
||||||
|
menu.clear();
|
||||||
|
// can't use sub-menus as AlertDialog doesn't support them
|
||||||
|
menu.add(MENU_CATEGORY, MENU_CONSTANT, Menu.NONE, R.string.c_vars_and_constants).setOnMenuItemClickListener(KeyboardUser.this);
|
||||||
|
menu.add(MENU_CATEGORY, MENU_FUNCTION, Menu.NONE, R.string.c_functions).setOnMenuItemClickListener(KeyboardUser.this);
|
||||||
|
unregisterForContextMenu(bodyView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bodyView.showContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertText(@NonNull CharSequence text, int selectionOffset) {
|
||||||
|
final int start = clampSelection(bodyView.getSelectionStart());
|
||||||
|
final int end = clampSelection(bodyView.getSelectionEnd());
|
||||||
|
final Editable e = bodyView.getText();
|
||||||
|
e.replace(start, end, text);
|
||||||
|
if (selectionOffset != 0) {
|
||||||
|
final int selection = clampSelection(bodyView.getSelectionEnd());
|
||||||
|
final int newSelection = selection + selectionOffset;
|
||||||
|
if (newSelection >= 0 && newSelection < e.length()) {
|
||||||
|
bodyView.setSelection(newSelection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVibrateOnKeypress() {
|
||||||
|
return keyboard.isVibrateOnKeypress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void done() {
|
||||||
|
keyboardWindow.hide();
|
||||||
|
validateBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showIme() {
|
||||||
|
final InputMethodManager keyboard = (InputMethodManager)
|
||||||
|
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
keyboard.showSoftInput(getEditor(), InputMethodManager.SHOW_FORCED);
|
||||||
|
keyboardWindow.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(final MenuItem item) {
|
||||||
|
final int groupId = item.getGroupId();
|
||||||
|
final CharSequence title = item.getTitle();
|
||||||
|
switch (groupId) {
|
||||||
|
case MENU_FUNCTION:
|
||||||
|
final int argsListIndex = title.toString().indexOf("(");
|
||||||
|
if (argsListIndex < 0) {
|
||||||
|
keyboardUser.insertText(title + "()", -1);
|
||||||
|
} else {
|
||||||
|
keyboardUser.insertText(title.subSequence(0, argsListIndex) + "()", -1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case MENU_CONSTANT:
|
||||||
|
keyboardUser.insertText(title.toString(), 0);
|
||||||
|
return true;
|
||||||
|
case MENU_CATEGORY:
|
||||||
|
bodyView.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final int itemId = item.getItemId();
|
||||||
|
if (itemId == MENU_FUNCTION) {
|
||||||
|
showFunctions(bodyView);
|
||||||
|
} else if (itemId == MENU_CONSTANT) {
|
||||||
|
showConstants(bodyView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean applyData(@NonNull CppFunction function);
|
||||||
|
}
|
@ -1,131 +1,33 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 serso aka se.solovyev
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
* Contact details
|
|
||||||
*
|
|
||||||
* Email: se.solovyev@gmail.com
|
|
||||||
* Site: http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.calculator.functions;
|
package org.solovyev.android.calculator.functions;
|
||||||
|
|
||||||
import static org.solovyev.android.calculator.functions.CppFunction.NO_ID;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.design.widget.TextInputLayout;
|
|
||||||
import android.support.v4.app.FragmentActivity;
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.ContextMenu;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewParent;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.EditText;
|
|
||||||
|
|
||||||
import org.solovyev.android.Activities;
|
import org.solovyev.android.Activities;
|
||||||
import org.solovyev.android.Check;
|
import org.solovyev.android.Check;
|
||||||
import org.solovyev.android.calculator.App;
|
import org.solovyev.android.calculator.App;
|
||||||
import org.solovyev.android.calculator.AppComponent;
|
|
||||||
import org.solovyev.android.calculator.BaseDialogFragment;
|
|
||||||
import org.solovyev.android.calculator.Calculator;
|
|
||||||
import org.solovyev.android.calculator.Engine;
|
|
||||||
import org.solovyev.android.calculator.FloatingCalculatorKeyboard;
|
|
||||||
import org.solovyev.android.calculator.Keyboard;
|
|
||||||
import org.solovyev.android.calculator.ParseException;
|
|
||||||
import org.solovyev.android.calculator.R;
|
import org.solovyev.android.calculator.R;
|
||||||
import org.solovyev.android.calculator.VariablesRegistry;
|
|
||||||
import org.solovyev.android.calculator.entities.EntityRemovalDialog;
|
import org.solovyev.android.calculator.entities.EntityRemovalDialog;
|
||||||
import org.solovyev.android.calculator.keyboard.FloatingKeyboardWindow;
|
|
||||||
import org.solovyev.android.calculator.view.EditTextCompat;
|
|
||||||
import org.solovyev.common.math.MathRegistry;
|
|
||||||
|
|
||||||
import butterknife.Bind;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import jscl.math.function.Function;
|
import jscl.math.function.Function;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class EditFunctionFragment extends BaseDialogFragment implements View.OnClickListener, View.OnFocusChangeListener, View.OnKeyListener {
|
public class EditFunctionFragment extends BaseFunctionFragment {
|
||||||
|
|
||||||
private static final String ARG_FUNCTION = "function";
|
public EditFunctionFragment() {
|
||||||
private static final int MENU_FUNCTION = Menu.FIRST;
|
super(R.layout.fragment_function_edit);
|
||||||
private static final int MENU_CONSTANT = Menu.FIRST + 1;
|
|
||||||
private static final int MENU_CATEGORY = Menu.FIRST + 2;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final FloatingKeyboardWindow keyboardWindow = new FloatingKeyboardWindow(null);
|
|
||||||
@NonNull
|
|
||||||
private final KeyboardUser keyboardUser = new KeyboardUser();
|
|
||||||
@Bind(R.id.function_params)
|
|
||||||
FunctionParamsView paramsView;
|
|
||||||
@Bind(R.id.function_name_label)
|
|
||||||
TextInputLayout nameLabel;
|
|
||||||
@Bind(R.id.function_name)
|
|
||||||
EditText nameView;
|
|
||||||
@Bind(R.id.function_body_label)
|
|
||||||
TextInputLayout bodyLabel;
|
|
||||||
@Bind(R.id.function_body)
|
|
||||||
EditTextCompat bodyView;
|
|
||||||
@Bind(R.id.function_description)
|
|
||||||
EditText descriptionView;
|
|
||||||
@Inject
|
|
||||||
Calculator calculator;
|
|
||||||
@Inject
|
|
||||||
Keyboard keyboard;
|
|
||||||
@Inject
|
|
||||||
FunctionsRegistry functionsRegistry;
|
|
||||||
@Inject
|
|
||||||
VariablesRegistry variablesRegistry;
|
|
||||||
@Nullable
|
|
||||||
private CppFunction function;
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private static EditFunctionFragment create(@Nullable CppFunction function) {
|
|
||||||
final EditFunctionFragment fragment = new EditFunctionFragment();
|
|
||||||
if (function != null) {
|
|
||||||
final Bundle args = new Bundle();
|
|
||||||
args.putParcelable(ARG_FUNCTION, function);
|
|
||||||
fragment.setArguments(args);
|
|
||||||
}
|
|
||||||
return fragment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void show(@Nonnull FragmentActivity activity) {
|
public static void show(@Nonnull FragmentActivity activity) {
|
||||||
EditFunctionFragment.show(null, activity.getSupportFragmentManager());
|
show(null, activity.getSupportFragmentManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void show(@Nullable CppFunction function, @Nonnull Context context) {
|
public static void show(@Nullable CppFunction function, @Nonnull Context context) {
|
||||||
@ -135,8 +37,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
|
|||||||
intent.putExtra(FunctionsActivity.EXTRA_FUNCTION, function);
|
intent.putExtra(FunctionsActivity.EXTRA_FUNCTION, function);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
EditFunctionFragment.show(function,
|
show(function, ((FunctionsActivity) context).getSupportFragmentManager());
|
||||||
((FunctionsActivity) context).getSupportFragmentManager());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,52 +45,25 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
|
|||||||
App.showDialog(create(function), "function-editor", fm);
|
App.showDialog(create(function), "function-editor", fm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Nonnull
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
private static BaseFunctionFragment create(@Nullable CppFunction function) {
|
||||||
super.onCreate(savedInstanceState);
|
final BaseFunctionFragment fragment = new EditFunctionFragment();
|
||||||
final Bundle arguments = getArguments();
|
if (function != null) {
|
||||||
if (arguments != null) {
|
final Bundle args = new Bundle();
|
||||||
function = arguments.getParcelable(ARG_FUNCTION);
|
args.putParcelable(ARG_FUNCTION, function);
|
||||||
|
fragment.setArguments(args);
|
||||||
}
|
}
|
||||||
}
|
return fragment;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(@NonNull AppComponent component) {
|
|
||||||
super.inject(component);
|
|
||||||
component.inject(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialog(@NonNull AlertDialog.Builder builder) {
|
protected void onPrepareDialog(@NonNull AlertDialog.Builder builder) {
|
||||||
builder.setNegativeButton(R.string.c_cancel, null);
|
super.onPrepareDialog(builder);
|
||||||
builder.setPositiveButton(R.string.ok, null);
|
|
||||||
builder.setTitle(isNewFunction() ? R.string.function_create_function :
|
|
||||||
R.string.function_edit_function);
|
|
||||||
if (!isNewFunction()) {
|
if (!isNewFunction()) {
|
||||||
builder.setNeutralButton(R.string.c_remove, null);
|
builder.setNeutralButton(R.string.c_remove, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNewFunction() {
|
|
||||||
return function == null || function.id == NO_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public AlertDialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
final AlertDialog dialog = super.onCreateDialog(savedInstanceState);
|
|
||||||
dialog.setCanceledOnTouchOutside(false);
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onShowDialog(@NonNull AlertDialog dialog, boolean firstTime) {
|
|
||||||
if (firstTime) {
|
|
||||||
nameView.selectAll();
|
|
||||||
showIme(nameView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showRemovalDialog(@NonNull final CppFunction function) {
|
private void showRemovalDialog(@NonNull final CppFunction function) {
|
||||||
EntityRemovalDialog.showForFunction(getActivity(), function.name,
|
EntityRemovalDialog.showForFunction(getActivity(), function.name,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@ -202,75 +76,9 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
|
||||||
if (v instanceof EditText && FunctionParamsView.PARAM_VIEW_TAG.equals(v.getTag())) {
|
|
||||||
final ViewParent parentView = v.getParent();
|
|
||||||
if (parentView instanceof TextInputLayout) {
|
|
||||||
if (hasFocus) {
|
|
||||||
clearError((TextInputLayout) parentView);
|
|
||||||
} else {
|
|
||||||
validateParameters();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int id = v.getId();
|
|
||||||
switch (id) {
|
|
||||||
case R.id.function_name:
|
|
||||||
if (hasFocus) {
|
|
||||||
clearError(nameLabel);
|
|
||||||
} else {
|
|
||||||
validateName();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case R.id.function_body:
|
|
||||||
if (hasFocus) {
|
|
||||||
clearError(bodyLabel);
|
|
||||||
showKeyboard();
|
|
||||||
} else {
|
|
||||||
keyboardWindow.hide();
|
|
||||||
validateBody();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showKeyboard() {
|
|
||||||
keyboardWindow.show(new FloatingCalculatorKeyboard(keyboardUser, collectParameters()),
|
|
||||||
getDialog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private List<String> collectParameters() {
|
|
||||||
final List<String> parameters = new ArrayList<>();
|
|
||||||
for (String parameter : paramsView.getParams()) {
|
|
||||||
if (!TextUtils.isEmpty(parameter)) {
|
|
||||||
parameters.add(parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
switch (v.getId()) {
|
|
||||||
case R.id.function_body:
|
|
||||||
showKeyboard();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
super.onClick(v);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case DialogInterface.BUTTON_POSITIVE:
|
|
||||||
tryClose();
|
|
||||||
break;
|
|
||||||
case DialogInterface.BUTTON_NEUTRAL:
|
case DialogInterface.BUTTON_NEUTRAL:
|
||||||
Check.isNotNull(function);
|
Check.isNotNull(function);
|
||||||
showRemovalDialog(function);
|
showRemovalDialog(function);
|
||||||
@ -282,32 +90,10 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
protected boolean applyData(@Nonnull @NonNull CppFunction function) {
|
||||||
if (v.getId() == R.id.function_body) {
|
|
||||||
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK && keyboardWindow.isShown()) {
|
|
||||||
keyboardWindow.hide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryClose() {
|
|
||||||
if (validate() && applyData()) {
|
|
||||||
dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean applyData() {
|
|
||||||
try {
|
try {
|
||||||
final String body = calculator.prepare(bodyView.getText().toString()).getValue();
|
|
||||||
|
|
||||||
final CppFunction newFunction = CppFunction.builder(nameView.getText().toString(), body)
|
|
||||||
.withId(isNewFunction() ? NO_ID : function.id)
|
|
||||||
.withParameters(collectParameters())
|
|
||||||
.withDescription(descriptionView.getText().toString()).build();
|
|
||||||
final Function oldFunction = isNewFunction() ? null : functionsRegistry.getById(function.id);
|
final Function oldFunction = isNewFunction() ? null : functionsRegistry.getById(function.id);
|
||||||
functionsRegistry.add(newFunction.toJsclBuilder(), oldFunction);
|
functionsRegistry.add(function.toJsclBuilder(), oldFunction);
|
||||||
return true;
|
return true;
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
setError(bodyLabel, e.getLocalizedMessage());
|
setError(bodyLabel, e.getLocalizedMessage());
|
||||||
@ -315,16 +101,12 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validate() {
|
@Override
|
||||||
return validateName() & validateParameters() & validateBody();
|
protected boolean validateName() {
|
||||||
}
|
if (!super.validateName()) {
|
||||||
|
|
||||||
private boolean validateName() {
|
|
||||||
final String name = nameView.getText().toString();
|
|
||||||
if (!Engine.isValidName(name)) {
|
|
||||||
setError(nameLabel, getString(R.string.function_name_is_not_valid));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
final String name = nameView.getText().toString();
|
||||||
final Function existingFunction = functionsRegistry.get(name);
|
final Function existingFunction = functionsRegistry.get(name);
|
||||||
if (existingFunction != null) {
|
if (existingFunction != null) {
|
||||||
if (!existingFunction.isIdDefined()) {
|
if (!existingFunction.isIdDefined()) {
|
||||||
@ -347,263 +129,4 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
|
|||||||
clearError(nameLabel);
|
clearError(nameLabel);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateBody() {
|
|
||||||
final String body = bodyView.getText().toString();
|
|
||||||
if (TextUtils.isEmpty(body)) {
|
|
||||||
setError(bodyLabel, getString(R.string.function_is_empty));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
calculator.prepare(body);
|
|
||||||
clearError(bodyLabel);
|
|
||||||
return true;
|
|
||||||
} catch (ParseException e) {
|
|
||||||
setError(bodyLabel, e.getLocalizedMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean validateParameters() {
|
|
||||||
boolean valid = true;
|
|
||||||
final List<String> parameters = paramsView.getParams();
|
|
||||||
final Set<String> usedParameters = new HashSet<>();
|
|
||||||
for (int i = 0; i < parameters.size(); i++) {
|
|
||||||
final String parameter = parameters.get(i);
|
|
||||||
final TextInputLayout paramLabel = paramsView.getParamLabel(i);
|
|
||||||
if (TextUtils.isEmpty(parameter)) {
|
|
||||||
clearError(paramLabel);
|
|
||||||
} else if (!Engine.isValidName(parameter)) {
|
|
||||||
valid = false;
|
|
||||||
setError(paramLabel, getString(R.string.invalid_name));
|
|
||||||
} else if (usedParameters.contains(parameter)) {
|
|
||||||
valid = false;
|
|
||||||
setError(paramLabel, getString(R.string.function_duplicate_parameter));
|
|
||||||
} else {
|
|
||||||
usedParameters.add(parameter);
|
|
||||||
clearError(paramLabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
protected View onCreateDialogView(@NonNull Context context, @NonNull LayoutInflater inflater, @Nullable Bundle savedInstanceState) {
|
|
||||||
final View view = inflater.inflate(R.layout.fragment_function_edit, null);
|
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
|
|
||||||
if (savedInstanceState == null && function != null) {
|
|
||||||
paramsView.addParams(function.getParameters());
|
|
||||||
nameView.setText(function.getName());
|
|
||||||
descriptionView.setText(function.getDescription());
|
|
||||||
bodyView.setText(function.getBody());
|
|
||||||
}
|
|
||||||
nameView.setOnFocusChangeListener(this);
|
|
||||||
paramsView.setOnFocusChangeListener(this);
|
|
||||||
bodyView.setOnClickListener(this);
|
|
||||||
bodyView.setOnFocusChangeListener(this);
|
|
||||||
bodyView.setOnKeyListener(this);
|
|
||||||
bodyView.dontShowSoftInputOnFocusCompat();
|
|
||||||
descriptionView.setOnFocusChangeListener(this);
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class KeyboardUser implements FloatingCalculatorKeyboard.User, MenuItem.OnMenuItemClickListener {
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Context getContext() {
|
|
||||||
return getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Resources getResources() {
|
|
||||||
return EditFunctionFragment.this.getResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public EditText getEditor() {
|
|
||||||
return bodyView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public ViewGroup getKeyboard() {
|
|
||||||
return keyboardWindow.getContentView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertOperator(char operator) {
|
|
||||||
insertOperator(String.valueOf(operator));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int clampSelection(int selection) {
|
|
||||||
return selection < 0 ? 0 : selection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertOperator(@NonNull String operator) {
|
|
||||||
final int start = clampSelection(bodyView.getSelectionStart());
|
|
||||||
final int end = clampSelection(bodyView.getSelectionEnd());
|
|
||||||
final Editable e = bodyView.getText();
|
|
||||||
e.replace(start, end, getOperator(start, end, e, operator));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private String getOperator(int start, int end, @NonNull Editable e, @NonNull CharSequence operator) {
|
|
||||||
boolean spaceBefore = true;
|
|
||||||
boolean spaceAfter = true;
|
|
||||||
if (start > 0 && Character.isSpaceChar(e.charAt(start - 1))) {
|
|
||||||
spaceBefore = false;
|
|
||||||
}
|
|
||||||
if (end < e.length() && Character.isSpaceChar(e.charAt(end))) {
|
|
||||||
spaceAfter = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spaceBefore && spaceAfter) {
|
|
||||||
return " " + operator + " ";
|
|
||||||
}
|
|
||||||
if (spaceBefore) {
|
|
||||||
return " " + operator;
|
|
||||||
}
|
|
||||||
if (spaceAfter) {
|
|
||||||
return operator + " ";
|
|
||||||
}
|
|
||||||
return String.valueOf(operator);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showConstants(@NonNull View v) {
|
|
||||||
bodyView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
|
|
||||||
@Override
|
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
|
||||||
final int id = v.getId();
|
|
||||||
if (id == R.id.function_body) {
|
|
||||||
menu.clear();
|
|
||||||
addEntities(menu, getNamesSorted(variablesRegistry), MENU_CONSTANT);
|
|
||||||
unregisterForContextMenu(bodyView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
bodyView.showContextMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private List<String> getNamesSorted(@NonNull MathRegistry<?> registry) {
|
|
||||||
final List<String> names = new ArrayList<>(registry.getNames());
|
|
||||||
Collections.sort(names);
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showFunctions(@NonNull View v) {
|
|
||||||
bodyView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
|
|
||||||
@Override
|
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
|
||||||
final int id = v.getId();
|
|
||||||
if (id == R.id.function_body) {
|
|
||||||
menu.clear();
|
|
||||||
addEntities(menu, getNamesSorted(functionsRegistry), MENU_FUNCTION);
|
|
||||||
unregisterForContextMenu(bodyView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
bodyView.showContextMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addEntities(@NonNull Menu menu, @NonNull List<String> entities, int groupId) {
|
|
||||||
for (String entity : entities) {
|
|
||||||
menu.add(groupId, Menu.NONE, Menu.NONE, entity).setOnMenuItemClickListener(KeyboardUser.this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showFunctionsConstants(@NonNull View v) {
|
|
||||||
bodyView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
|
|
||||||
@Override
|
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
|
||||||
final int id = v.getId();
|
|
||||||
if (id == R.id.function_body) {
|
|
||||||
menu.clear();
|
|
||||||
// can't use sub-menus as AlertDialog doesn't support them
|
|
||||||
menu.add(MENU_CATEGORY, MENU_CONSTANT, Menu.NONE, R.string.c_vars_and_constants).setOnMenuItemClickListener(KeyboardUser.this);
|
|
||||||
menu.add(MENU_CATEGORY, MENU_FUNCTION, Menu.NONE, R.string.c_functions).setOnMenuItemClickListener(KeyboardUser.this);
|
|
||||||
unregisterForContextMenu(bodyView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
bodyView.showContextMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertText(@NonNull CharSequence text, int selectionOffset) {
|
|
||||||
final int start = clampSelection(bodyView.getSelectionStart());
|
|
||||||
final int end = clampSelection(bodyView.getSelectionEnd());
|
|
||||||
final Editable e = bodyView.getText();
|
|
||||||
e.replace(start, end, text);
|
|
||||||
if (selectionOffset != 0) {
|
|
||||||
final int selection = clampSelection(bodyView.getSelectionEnd());
|
|
||||||
final int newSelection = selection + selectionOffset;
|
|
||||||
if (newSelection >= 0 && newSelection < e.length()) {
|
|
||||||
bodyView.setSelection(newSelection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVibrateOnKeypress() {
|
|
||||||
return keyboard.isVibrateOnKeypress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void done() {
|
|
||||||
keyboardWindow.hide();
|
|
||||||
validateBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showIme() {
|
|
||||||
final InputMethodManager keyboard = (InputMethodManager)
|
|
||||||
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
keyboard.showSoftInput(getEditor(), InputMethodManager.SHOW_FORCED);
|
|
||||||
keyboardWindow.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMenuItemClick(final MenuItem item) {
|
|
||||||
final int groupId = item.getGroupId();
|
|
||||||
final CharSequence title = item.getTitle();
|
|
||||||
switch (groupId) {
|
|
||||||
case MENU_FUNCTION:
|
|
||||||
final int argsListIndex = title.toString().indexOf("(");
|
|
||||||
if (argsListIndex < 0) {
|
|
||||||
keyboardUser.insertText(title + "()", -1);
|
|
||||||
} else {
|
|
||||||
keyboardUser.insertText(title.subSequence(0, argsListIndex) + "()", -1);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case MENU_CONSTANT:
|
|
||||||
keyboardUser.insertText(title.toString(), 0);
|
|
||||||
return true;
|
|
||||||
case MENU_CATEGORY:
|
|
||||||
bodyView.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
final int itemId = item.getItemId();
|
|
||||||
if (itemId == MENU_FUNCTION) {
|
|
||||||
showFunctions(bodyView);
|
|
||||||
} else if (itemId == MENU_CONSTANT) {
|
|
||||||
showConstants(bodyView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,15 @@ import android.content.DialogInterface;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.FragmentActivity;
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.view.*;
|
import android.view.ContextMenu;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.squareup.otto.Bus;
|
import com.squareup.otto.Bus;
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
import jscl.math.function.Function;
|
|
||||||
import jscl.math.function.IFunction;
|
|
||||||
import org.solovyev.android.Check;
|
import org.solovyev.android.Check;
|
||||||
import org.solovyev.android.calculator.AppComponent;
|
import org.solovyev.android.calculator.AppComponent;
|
||||||
import org.solovyev.android.calculator.Calculator;
|
import org.solovyev.android.calculator.Calculator;
|
||||||
@ -39,11 +43,15 @@ import org.solovyev.android.calculator.entities.BaseEntitiesFragment;
|
|||||||
import org.solovyev.android.calculator.entities.Category;
|
import org.solovyev.android.calculator.entities.Category;
|
||||||
import org.solovyev.android.calculator.entities.EntityRemovalDialog;
|
import org.solovyev.android.calculator.entities.EntityRemovalDialog;
|
||||||
|
|
||||||
|
import jscl.math.function.Function;
|
||||||
|
import jscl.math.function.IFunction;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class FunctionsFragment extends BaseEntitiesFragment<Function> {
|
public class FunctionsFragment extends BaseEntitiesFragment<Function> {
|
||||||
|
|
||||||
@ -85,7 +93,8 @@ public class FunctionsFragment extends BaseEntitiesFragment<Function> {
|
|||||||
return true;
|
return true;
|
||||||
case R.string.c_edit:
|
case R.string.c_edit:
|
||||||
if (function instanceof IFunction) {
|
if (function instanceof IFunction) {
|
||||||
EditFunctionFragment.show(CppFunction.builder((IFunction) function).build(), activity.getSupportFragmentManager());
|
EditFunctionFragment.show(CppFunction.builder((IFunction) function).build(),
|
||||||
|
activity.getSupportFragmentManager());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.string.c_remove:
|
case R.string.c_remove:
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import org.solovyev.android.plotter.Function;
|
||||||
|
|
||||||
|
import jscl.math.Expression;
|
||||||
|
import jscl.math.Generic;
|
||||||
|
import jscl.math.JsclInteger;
|
||||||
|
import jscl.math.NumericWrapper;
|
||||||
|
import jscl.math.function.Constant;
|
||||||
|
import jscl.math.numeric.Complex;
|
||||||
|
import jscl.math.numeric.Numeric;
|
||||||
|
import jscl.math.numeric.Real;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class ExpressionFunction extends Function {
|
||||||
|
private static final Complex NaN = Complex.valueOf(Double.NaN, 0d);
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public final jscl.math.function.Function function;
|
||||||
|
public final Constant xVariable;
|
||||||
|
public final Constant yVariable;
|
||||||
|
public final boolean imaginary;
|
||||||
|
public final int arity;
|
||||||
|
|
||||||
|
public ExpressionFunction(@Nonnull jscl.math.function.Function function, @Nullable Constant x,
|
||||||
|
@Nullable Constant y, boolean imaginary) {
|
||||||
|
super(imaginary ? "Im(" + function.toString() + ")" : function.toString());
|
||||||
|
this.function = function;
|
||||||
|
this.xVariable = x;
|
||||||
|
this.yVariable = y;
|
||||||
|
this.imaginary = imaginary;
|
||||||
|
this.arity = countArity(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int countArity(@Nullable Constant x, @Nullable Constant y) {
|
||||||
|
if (x != null && y != null) {
|
||||||
|
return 2;
|
||||||
|
} else if (x == null && y == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArity() {
|
||||||
|
return arity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float evaluate() {
|
||||||
|
final Complex value = calculate(function);
|
||||||
|
if (imaginary) {
|
||||||
|
return (float) value.imaginaryPart();
|
||||||
|
}
|
||||||
|
return (float) value.realPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float evaluate(float x) {
|
||||||
|
final Complex value = calculate(function, xVariable, x);
|
||||||
|
if (imaginary) {
|
||||||
|
return (float) value.imaginaryPart();
|
||||||
|
}
|
||||||
|
return (float) value.realPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float evaluate(float x, float y) {
|
||||||
|
final Complex value = calculate(function, xVariable, x, yVariable, y);
|
||||||
|
if (imaginary) {
|
||||||
|
return (float) value.imaginaryPart();
|
||||||
|
}
|
||||||
|
return (float) value.realPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static Complex calculate(jscl.math.function.Function function, Constant xVar,
|
||||||
|
float x, Constant yVar, float y) {
|
||||||
|
try {
|
||||||
|
Generic tmp = function.substitute(xVar, Expression.valueOf((double) x));
|
||||||
|
tmp = tmp.substitute(yVar, Expression.valueOf((double) y));
|
||||||
|
return unwrap(tmp.numeric());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static Complex calculate(jscl.math.function.Function function, Constant xVar,
|
||||||
|
float x) {
|
||||||
|
try {
|
||||||
|
return unwrap(function.substitute(xVar, Expression.valueOf((double) x)).numeric());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static Complex calculate(jscl.math.function.Function function) {
|
||||||
|
try {
|
||||||
|
return unwrap(function.numeric());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static Complex unwrap(Generic numeric) {
|
||||||
|
if (numeric instanceof JsclInteger) {
|
||||||
|
return Complex.valueOf(((JsclInteger) numeric).intValue(), 0d);
|
||||||
|
} else if (numeric instanceof NumericWrapper) {
|
||||||
|
return unwrap(((NumericWrapper) numeric).content());
|
||||||
|
} else {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static Complex unwrap(Numeric content) {
|
||||||
|
if (content instanceof Real) {
|
||||||
|
return Complex.valueOf(((Real) content).doubleValue(), 0d);
|
||||||
|
} else if (content instanceof Complex) {
|
||||||
|
return ((Complex) content);
|
||||||
|
} else {
|
||||||
|
throw new ArithmeticException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v4.app.FragmentTransaction;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.AppComponent;
|
||||||
|
import org.solovyev.android.calculator.BaseActivity;
|
||||||
|
import org.solovyev.android.calculator.BaseFragment;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
import org.solovyev.android.plotter.Dimensions;
|
||||||
|
import org.solovyev.android.plotter.PlotViewFrame;
|
||||||
|
import org.solovyev.android.plotter.Plotter;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class PlotActivity extends BaseActivity {
|
||||||
|
|
||||||
|
public static class MyFragment extends BaseFragment implements PlotViewFrame.Listener {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Plotter plotter;
|
||||||
|
@Bind(R.id.plot_view_frame)
|
||||||
|
PlotViewFrame plotView;
|
||||||
|
|
||||||
|
public MyFragment() {
|
||||||
|
super(R.layout.fragment_plot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(@Nonnull AppComponent component) {
|
||||||
|
super.inject(component);
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
|
plotView.addControlView(R.id.plot_add_function);
|
||||||
|
plotView.addControlView(R.id.plot_functions);
|
||||||
|
plotView.addControlView(R.id.plot_dimensions);
|
||||||
|
plotView.setPlotter(plotter);
|
||||||
|
plotView.setListener(this);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
plotView.onPause();
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
plotView.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onButtonPressed(int id) {
|
||||||
|
if (id == R.id.plot_dimensions) {
|
||||||
|
final Dimensions dimensions = plotter.getDimensions();
|
||||||
|
PlotDimensionsFragment.show(dimensions.graph.makeBounds(), plotter.is3d(),
|
||||||
|
getActivity().getSupportFragmentManager());
|
||||||
|
return true;
|
||||||
|
} else if (id == R.id.plot_functions) {
|
||||||
|
PlotFunctionsFragment.show(getActivity().getSupportFragmentManager());
|
||||||
|
return true;
|
||||||
|
} else if (id == R.id.plot_add_function) {
|
||||||
|
//App.getBus().post(new AddFunctionDialog.ShowEvent());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unableToZoom(boolean in) {
|
||||||
|
Toast.makeText(getActivity(), "Can't zoom anymore", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlotActivity() {
|
||||||
|
super(R.layout.activity_empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
final FragmentManager fm = getSupportFragmentManager();
|
||||||
|
final FragmentTransaction t = fm.beginTransaction();
|
||||||
|
t.add(R.id.main, new MyFragment(), "plotter");
|
||||||
|
t.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,288 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.design.widget.TextInputLayout;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.App;
|
||||||
|
import org.solovyev.android.calculator.AppComponent;
|
||||||
|
import org.solovyev.android.calculator.BaseDialogFragment;
|
||||||
|
import org.solovyev.android.calculator.BaseFragment;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
import org.solovyev.android.plotter.Check;
|
||||||
|
import org.solovyev.android.plotter.Plot;
|
||||||
|
import org.solovyev.android.plotter.Plotter;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class PlotDimensionsFragment extends BaseDialogFragment
|
||||||
|
implements TextView.OnEditorActionListener {
|
||||||
|
private static final String ARG_BOUNDS = "arg-bounds";
|
||||||
|
private static final String ARG_3D = "arg-3d";
|
||||||
|
|
||||||
|
private class MyTextWatcher implements TextWatcher {
|
||||||
|
@NonNull
|
||||||
|
private final TextInputLayout input;
|
||||||
|
private final boolean x;
|
||||||
|
|
||||||
|
private MyTextWatcher(@NonNull TextInputLayout input, boolean x) {
|
||||||
|
this.input = input;
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
if (TextUtils.isEmpty(input.getError())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final RectF bounds = collectData();
|
||||||
|
if (x) {
|
||||||
|
validXBounds(bounds);
|
||||||
|
} else {
|
||||||
|
validYBounds(bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Plotter plotter;
|
||||||
|
@Bind(R.id.plot_x_min)
|
||||||
|
EditText xMin;
|
||||||
|
@Bind(R.id.plot_x_min_label)
|
||||||
|
TextInputLayout xMinLabel;
|
||||||
|
@Bind(R.id.plot_x_max)
|
||||||
|
EditText xMax;
|
||||||
|
@Bind(R.id.plot_x_max_label)
|
||||||
|
TextInputLayout xMaxLabel;
|
||||||
|
@Bind(R.id.plot_y_min)
|
||||||
|
EditText yMin;
|
||||||
|
@Bind(R.id.plot_y_min_label)
|
||||||
|
TextInputLayout yMinLabel;
|
||||||
|
@Bind(R.id.plot_y_max)
|
||||||
|
EditText yMax;
|
||||||
|
@Bind(R.id.plot_y_max_label)
|
||||||
|
TextInputLayout yMaxLabel;
|
||||||
|
@Bind(R.id.y_bounds)
|
||||||
|
View yBounds;
|
||||||
|
@NonNull
|
||||||
|
private RectF bounds = new RectF();
|
||||||
|
private boolean d3;
|
||||||
|
|
||||||
|
public PlotDimensionsFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(@NonNull RectF bounds, boolean d3, @Nonnull FragmentManager fm) {
|
||||||
|
App.showDialog(create(bounds, d3), "plot-dimensions", fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static PlotDimensionsFragment create(@NonNull RectF bounds, boolean d3) {
|
||||||
|
final PlotDimensionsFragment dialog = new PlotDimensionsFragment();
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putParcelable(ARG_BOUNDS, bounds);
|
||||||
|
args.putBoolean(ARG_3D, d3);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
Check.isNotNull(arguments);
|
||||||
|
bounds = BaseFragment.getParcelable(arguments, ARG_BOUNDS);
|
||||||
|
d3 = arguments.getBoolean(ARG_3D);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(@NonNull AppComponent component) {
|
||||||
|
super.inject(component);
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AlertDialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final AlertDialog dialog = super.onCreateDialog(savedInstanceState);
|
||||||
|
dialog.setCanceledOnTouchOutside(false);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShowDialog(@NonNull AlertDialog dialog, boolean firstTime) {
|
||||||
|
super.onShowDialog(dialog, firstTime);
|
||||||
|
if (firstTime) {
|
||||||
|
final InputMethodManager imm = (InputMethodManager) getActivity()
|
||||||
|
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.showSoftInput(xMin, InputMethodManager.SHOW_IMPLICIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPrepareDialog(@NonNull AlertDialog.Builder builder) {
|
||||||
|
builder.setTitle("Dimensions");
|
||||||
|
builder.setPositiveButton(android.R.string.ok, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected View onCreateDialogView(@NonNull Context context, @NonNull LayoutInflater inflater,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
@SuppressLint("InflateParams") final View view =
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.fragment_plot_dimensions, null);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
|
setDimension(xMin, bounds.left);
|
||||||
|
setDimension(xMax, bounds.right);
|
||||||
|
setDimension(yMin, bounds.top);
|
||||||
|
setDimension(yMax, bounds.bottom);
|
||||||
|
xMin.addTextChangedListener(new MyTextWatcher(xMinLabel, true));
|
||||||
|
xMax.addTextChangedListener(new MyTextWatcher(xMaxLabel, true));
|
||||||
|
yMin.addTextChangedListener(new MyTextWatcher(yMinLabel, false));
|
||||||
|
yMax.addTextChangedListener(new MyTextWatcher(yMaxLabel, false));
|
||||||
|
if (d3) {
|
||||||
|
yBounds.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
switch (which) {
|
||||||
|
case DialogInterface.BUTTON_POSITIVE:
|
||||||
|
tryClose();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
super.onClick(dialog, which);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDimension(@NonNull EditText view, float value) {
|
||||||
|
view.setOnEditorActionListener(this);
|
||||||
|
view.setText(String.format("%.2f", value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryClose() {
|
||||||
|
if (validate()) {
|
||||||
|
applyData();
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validate() {
|
||||||
|
final RectF bounds = collectData();
|
||||||
|
if (!validXBounds(bounds) | !validYBounds(bounds)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validYBounds(@NonNull RectF bounds) {
|
||||||
|
if (validNumbers(this.bounds.top, this.bounds.bottom, yMinLabel, yMaxLabel)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bounds.top >= bounds.bottom) {
|
||||||
|
setError(yMinLabel, " ");
|
||||||
|
setError(yMaxLabel, "max ≯ min");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
clearError(yMinLabel);
|
||||||
|
clearError(yMaxLabel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validXBounds(@NonNull RectF bounds) {
|
||||||
|
if (validNumbers(bounds.left, bounds.right, xMinLabel, xMaxLabel)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bounds.left >= bounds.right) {
|
||||||
|
setError(xMinLabel, " ");
|
||||||
|
setError(xMaxLabel, "max ≯ min");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
clearError(xMinLabel);
|
||||||
|
clearError(xMaxLabel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validNumbers(float l, float r, @NonNull TextInputLayout lInput, @NonNull
|
||||||
|
TextInputLayout rInput) {
|
||||||
|
final boolean nanLeft = Float.isNaN(l);
|
||||||
|
final boolean nanRight = Float.isNaN(r);
|
||||||
|
if (nanLeft || nanRight) {
|
||||||
|
if (nanLeft) {
|
||||||
|
setError(lInput, " ");
|
||||||
|
} else {
|
||||||
|
clearError(lInput);
|
||||||
|
}
|
||||||
|
if (nanRight) {
|
||||||
|
setError(rInput, " ");
|
||||||
|
} else {
|
||||||
|
clearError(rInput);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private RectF collectData() {
|
||||||
|
return new RectF(getDimension(xMin), getDimension(yMin), getDimension(xMax),
|
||||||
|
getDimension(yMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyData() {
|
||||||
|
final RectF bounds = collectData();
|
||||||
|
Plot.setGraphBounds(null, plotter, bounds, d3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getDimension(@NonNull EditText view) {
|
||||||
|
try {
|
||||||
|
return Float.parseFloat(view.getText().toString().replace(",", ".").replace("−", "-"));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(Plot.getTag("MainActivity"), e.getMessage(), e);
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
|
tryClose();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,201 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.App;
|
||||||
|
import org.solovyev.android.calculator.AppComponent;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
import org.solovyev.android.calculator.functions.BaseFunctionFragment;
|
||||||
|
import org.solovyev.android.calculator.functions.CppFunction;
|
||||||
|
import org.solovyev.android.plotter.Color;
|
||||||
|
import org.solovyev.android.plotter.PlotFunction;
|
||||||
|
import org.solovyev.android.plotter.PlotIconView;
|
||||||
|
import org.solovyev.android.plotter.Plotter;
|
||||||
|
import org.solovyev.android.plotter.meshes.MeshSpec;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import jscl.math.function.Constant;
|
||||||
|
import jscl.math.function.CustomFunction;
|
||||||
|
import uz.shift.colorpicker.LineColorPicker;
|
||||||
|
import uz.shift.colorpicker.OnColorChangedListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class PlotEditFunctionFragment extends BaseFunctionFragment
|
||||||
|
implements SeekBar.OnSeekBarChangeListener {
|
||||||
|
@Inject
|
||||||
|
Plotter plotter;
|
||||||
|
@Bind(R.id.fn_meshspec_views)
|
||||||
|
View meshSpecViews;
|
||||||
|
@Bind(R.id.fn_color_label)
|
||||||
|
TextView colorLabel;
|
||||||
|
@Bind(R.id.fn_color_picker)
|
||||||
|
LineColorPicker colorPicker;
|
||||||
|
@Bind(R.id.fn_linewidth_label)
|
||||||
|
TextView lineWidthLabel;
|
||||||
|
@Bind(R.id.fn_linewidth_seekbar)
|
||||||
|
SeekBar lineWidthSeekBar;
|
||||||
|
@Bind(R.id.fn_iconview)
|
||||||
|
PlotIconView iconView;
|
||||||
|
private PlotFunction plotFunction;
|
||||||
|
|
||||||
|
public PlotEditFunctionFragment() {
|
||||||
|
super(R.layout.fragment_plot_function_edit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(@Nullable PlotFunction function, @Nonnull
|
||||||
|
FragmentManager fm) {
|
||||||
|
App.showDialog(create(function), "plot-function-editor", fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static PlotEditFunctionFragment create(@Nullable PlotFunction pf) {
|
||||||
|
final PlotEditFunctionFragment fragment = new PlotEditFunctionFragment();
|
||||||
|
if (pf != null && pf.function instanceof ExpressionFunction) {
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
final String name =
|
||||||
|
pf.function.hasName() ? Strings.nullToEmpty(pf.function.getName()) : "";
|
||||||
|
final List<String> parameters = new ArrayList<>();
|
||||||
|
final ExpressionFunction ef = (ExpressionFunction) pf.function;
|
||||||
|
if (ef.xVariable != null) {
|
||||||
|
parameters.add(ef.xVariable.getName());
|
||||||
|
}
|
||||||
|
if (ef.yVariable != null) {
|
||||||
|
parameters.add(ef.yVariable.getName());
|
||||||
|
}
|
||||||
|
args.putParcelable(ARG_FUNCTION, CppFunction
|
||||||
|
.builder(name,
|
||||||
|
((CustomFunction) ef.function).getContent())
|
||||||
|
.withParameters(parameters)
|
||||||
|
.withId(pf.function.getId())
|
||||||
|
.build());
|
||||||
|
fragment.setArguments(args);
|
||||||
|
}
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (function != null) {
|
||||||
|
plotFunction = plotter.getPlotData().get(function.getId());
|
||||||
|
if (plotFunction == null) {
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(@NonNull AppComponent component) {
|
||||||
|
super.inject(component);
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected View onCreateDialogView(@NonNull Context context, @NonNull LayoutInflater inflater,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
final View view = super.onCreateDialogView(context, inflater, savedInstanceState);
|
||||||
|
colorPicker.setOnColorChangedListener(new OnColorChangedListener() {
|
||||||
|
@Override
|
||||||
|
public void onColorChanged(int c) {
|
||||||
|
iconView.setMeshSpec(applyMeshSpec());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lineWidthSeekBar.setMax(MeshSpec.MAX_WIDTH - MeshSpec.MIN_WIDTH);
|
||||||
|
lineWidthSeekBar.setOnSeekBarChangeListener(this);
|
||||||
|
|
||||||
|
final int[] colors = MeshSpec.LightColors.asIntArray();
|
||||||
|
colorPicker.setColors(colors);
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
if (plotFunction != null) {
|
||||||
|
setupViews(plotFunction.meshSpec);
|
||||||
|
} else {
|
||||||
|
setupViews();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupViews(@NonNull MeshSpec meshSpec) {
|
||||||
|
final int color = meshSpec.color.toInt();
|
||||||
|
final int[] colors = colorPicker.getColors();
|
||||||
|
final int i = indexOf(colors, color);
|
||||||
|
colorPicker.setSelectedColorPosition(Math.max(0, i));
|
||||||
|
lineWidthSeekBar.setProgress(meshSpec.width - MeshSpec.MIN_WIDTH);
|
||||||
|
iconView.setMeshSpec(meshSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupViews() {
|
||||||
|
colorPicker.setSelectedColorPosition(0);
|
||||||
|
lineWidthSeekBar.setProgress(MeshSpec.defaultWidth(getActivity()) - MeshSpec.MIN_WIDTH);
|
||||||
|
iconView.setMeshSpec(applyMeshSpec());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int indexOf(int[] integers, int integer) {
|
||||||
|
for (int i = 0; i < integers.length; i++) {
|
||||||
|
if (integers[i] == integer) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected MeshSpec applyMeshSpec() {
|
||||||
|
final Color color = Color.create(colorPicker.getColor());
|
||||||
|
final int width = MeshSpec.MIN_WIDTH + lineWidthSeekBar.getProgress();
|
||||||
|
return MeshSpec.create(color, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean applyData(@Nonnull CppFunction function) {
|
||||||
|
try {
|
||||||
|
final List<String> parameters = function.getParameters();
|
||||||
|
final Constant x = parameters.size() > 0 ? new Constant(parameters.get(0)) : null;
|
||||||
|
final Constant y = parameters.size() > 1 ? new Constant(parameters.get(1)) : null;
|
||||||
|
final ExpressionFunction expressionFunction =
|
||||||
|
new ExpressionFunction(function.toJsclBuilder().create(), x, y, false);
|
||||||
|
final PlotFunction plotFunction = PlotFunction.create(expressionFunction,
|
||||||
|
applyMeshSpec());
|
||||||
|
final int id = function.getId();
|
||||||
|
if (id != CppFunction.NO_ID) {
|
||||||
|
plotter.update(id, plotFunction);
|
||||||
|
} else {
|
||||||
|
plotter.add(plotFunction);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
setError(bodyLabel, e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
iconView.setMeshSpec(applyMeshSpec());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,232 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
|
||||||
|
import static android.view.Menu.NONE;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.App;
|
||||||
|
import org.solovyev.android.calculator.AppComponent;
|
||||||
|
import org.solovyev.android.calculator.BaseDialogFragment;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
import org.solovyev.android.plotter.BasePlotterListener;
|
||||||
|
import org.solovyev.android.plotter.PlotFunction;
|
||||||
|
import org.solovyev.android.plotter.PlotIconView;
|
||||||
|
import org.solovyev.android.plotter.Plotter;
|
||||||
|
import org.solovyev.android.views.llm.DividerItemDecoration;
|
||||||
|
import org.solovyev.android.views.llm.LinearLayoutManager;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class PlotFunctionsFragment extends BaseDialogFragment {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Plotter plotter;
|
||||||
|
@NonNull
|
||||||
|
private final PlotterListener plotterListener = new PlotterListener();
|
||||||
|
private Adapter adapter;
|
||||||
|
|
||||||
|
public PlotFunctionsFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(@Nonnull FragmentManager fm) {
|
||||||
|
App.showDialog(new PlotFunctionsFragment(), "plot-functions", fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(@NonNull AppComponent component) {
|
||||||
|
super.inject(component);
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
plotter.addListener(plotterListener);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected RecyclerView onCreateDialogView(@NonNull Context context, @NonNull LayoutInflater inflater, Bundle savedInstanceState) {
|
||||||
|
@SuppressLint("InflateParams") final RecyclerView view = (RecyclerView) inflater.inflate(R.layout.dialog_functions, null);
|
||||||
|
|
||||||
|
final LinearLayoutManager layoutManager = new LinearLayoutManager(context, VERTICAL, false);
|
||||||
|
final int itemHeight = context.getResources().getDimensionPixelSize(R.dimen.list_item_height);
|
||||||
|
layoutManager.setChildSize(itemHeight + getDividerHeight(context));
|
||||||
|
view.setLayoutManager(layoutManager);
|
||||||
|
|
||||||
|
view.addItemDecoration(new DividerItemDecoration(context, null));
|
||||||
|
adapter = new Adapter(plotter.getPlotData().functions);
|
||||||
|
view.setAdapter(adapter);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getDividerHeight(@NonNull Context context) {
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(null, new int[]{android.R.attr.listDivider});
|
||||||
|
final Drawable divider = a.getDrawable(0);
|
||||||
|
final int dividerHeight = divider == null ? 0 : divider.getIntrinsicHeight();
|
||||||
|
a.recycle();
|
||||||
|
return dividerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
plotter.removeListener(plotterListener);
|
||||||
|
super.onDestroyView();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPrepareDialog(@NonNull AlertDialog.Builder builder) {
|
||||||
|
builder.setPositiveButton(android.R.string.ok, null);
|
||||||
|
builder.setNeutralButton("Add", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
switch (which) {
|
||||||
|
case DialogInterface.BUTTON_NEUTRAL:
|
||||||
|
PlotEditFunctionFragment.show(null, getActivity().getSupportFragmentManager());
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
super.onClick(dialog, which);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener {
|
||||||
|
|
||||||
|
@Bind(R.id.function_icon)
|
||||||
|
PlotIconView icon;
|
||||||
|
|
||||||
|
@Bind(R.id.fn_name_edittext)
|
||||||
|
TextView name;
|
||||||
|
private PlotFunction function;
|
||||||
|
|
||||||
|
private ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
ButterKnife.bind(this, itemView);
|
||||||
|
itemView.setOnClickListener(this);
|
||||||
|
itemView.setOnCreateContextMenuListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(@NonNull PlotFunction function) {
|
||||||
|
this.function = function;
|
||||||
|
name.setText(function.function.getName());
|
||||||
|
icon.setMeshSpec(function.meshSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
PlotEditFunctionFragment.show(function, getActivity().getSupportFragmentManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
|
menu.add(NONE, R.string.c_remove, NONE, R.string.c_remove).setOnMenuItemClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
if (function != null && item.getItemId() == R.string.c_remove) {
|
||||||
|
plotter.remove(function);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Adapter extends RecyclerView.Adapter {
|
||||||
|
@NonNull
|
||||||
|
private final List<PlotFunction> list;
|
||||||
|
|
||||||
|
public Adapter(@NonNull List<PlotFunction> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||||
|
return new ViewHolder(inflater.inflate(R.layout.dialog_functions_function, parent, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||||
|
((ViewHolder) holder).bind(list.get(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(@NonNull PlotFunction function) {
|
||||||
|
final int i = list.indexOf(function);
|
||||||
|
if (i >= 0) {
|
||||||
|
list.remove(i);
|
||||||
|
notifyItemRemoved(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(int id, @NonNull PlotFunction function) {
|
||||||
|
final int i = find(id);
|
||||||
|
if (i >= 0) {
|
||||||
|
list.set(i, function);
|
||||||
|
notifyItemChanged(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int find(int id) {
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
final PlotFunction function = list.get(i);
|
||||||
|
if (function.function.getId() == id) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NonNull PlotFunction function) {
|
||||||
|
list.add(function);
|
||||||
|
notifyItemInserted(list.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PlotterListener extends BasePlotterListener {
|
||||||
|
@Override
|
||||||
|
public void onFunctionAdded(@NonNull PlotFunction function) {
|
||||||
|
adapter.add(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFunctionUpdated(int id, @NonNull PlotFunction function) {
|
||||||
|
adapter.update(id, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFunctionRemoved(@NonNull PlotFunction function) {
|
||||||
|
adapter.remove(function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
405
app/src/main/java/uz/shift/colorpicker/LineColorPicker.java
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
package uz.shift.colorpicker;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Paint.Style;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
|
||||||
|
|
||||||
|
public class LineColorPicker extends View {
|
||||||
|
|
||||||
|
public static final int HORIZONTAL = 0;
|
||||||
|
public static final int VERTICAL = 1;
|
||||||
|
|
||||||
|
int[] colors = isInEditMode() ? Palette.DEFAULT : new int[1];
|
||||||
|
// indicate if nothing selected
|
||||||
|
boolean isColorSelected = false;
|
||||||
|
private Paint paint;
|
||||||
|
private Rect rect = new Rect();
|
||||||
|
private int selectedColor = colors[0];
|
||||||
|
private OnColorChangedListener onColorChanged;
|
||||||
|
private int cellSize;
|
||||||
|
private int mOrientation = HORIZONTAL;
|
||||||
|
private boolean isClick = false;
|
||||||
|
private int screenW;
|
||||||
|
private int screenH;
|
||||||
|
|
||||||
|
public LineColorPicker(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
paint = new Paint();
|
||||||
|
paint.setStyle(Style.FILL);
|
||||||
|
|
||||||
|
final TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LineColorPicker, 0, 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
mOrientation = a.getInteger(R.styleable.LineColorPicker_lcp_orientation, HORIZONTAL);
|
||||||
|
|
||||||
|
if (!isInEditMode()) {
|
||||||
|
final int colorsArrayResId = a.getResourceId(R.styleable.LineColorPicker_lcp_colors, -1);
|
||||||
|
|
||||||
|
if (colorsArrayResId > 0) {
|
||||||
|
final int[] colors = context.getResources().getIntArray(colorsArrayResId);
|
||||||
|
setColors(colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int selected = a.getInteger(R.styleable.LineColorPicker_lcp_selectedColorIndex, -1);
|
||||||
|
|
||||||
|
if (selected != -1) {
|
||||||
|
final int[] currentColors = getColors();
|
||||||
|
|
||||||
|
final int currentColorsLength = currentColors != null ? currentColors.length : 0;
|
||||||
|
|
||||||
|
if (selected < currentColorsLength) {
|
||||||
|
setSelectedColorPosition(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
if (mOrientation == HORIZONTAL) {
|
||||||
|
drawHorizontalPicker(canvas);
|
||||||
|
} else {
|
||||||
|
drawVerticalPicker(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawVerticalPicker(Canvas canvas) {
|
||||||
|
rect.left = 0;
|
||||||
|
rect.top = 0;
|
||||||
|
rect.right = getWidth();
|
||||||
|
rect.bottom = 0;
|
||||||
|
|
||||||
|
// 8%
|
||||||
|
int margin = Math.round(getWidth() * 0.08f);
|
||||||
|
|
||||||
|
for (int i = 0; i < colors.length; i++) {
|
||||||
|
|
||||||
|
paint.setColor(colors[i]);
|
||||||
|
|
||||||
|
rect.top = rect.bottom;
|
||||||
|
rect.bottom += cellSize;
|
||||||
|
|
||||||
|
if (isColorSelected && colors[i] == selectedColor) {
|
||||||
|
rect.left = 0;
|
||||||
|
rect.right = getWidth();
|
||||||
|
} else {
|
||||||
|
rect.left = margin;
|
||||||
|
rect.right = getWidth() - margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawRect(rect, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawHorizontalPicker(Canvas canvas) {
|
||||||
|
rect.left = 0;
|
||||||
|
rect.top = 0;
|
||||||
|
rect.right = 0;
|
||||||
|
rect.bottom = getHeight();
|
||||||
|
|
||||||
|
// 8%
|
||||||
|
int margin = Math.round(getHeight() * 0.08f);
|
||||||
|
|
||||||
|
for (int i = 0; i < colors.length; i++) {
|
||||||
|
|
||||||
|
paint.setColor(colors[i]);
|
||||||
|
|
||||||
|
rect.left = rect.right;
|
||||||
|
rect.right += cellSize;
|
||||||
|
|
||||||
|
if (isColorSelected && colors[i] == selectedColor) {
|
||||||
|
rect.top = 0;
|
||||||
|
rect.bottom = getHeight();
|
||||||
|
} else {
|
||||||
|
rect.top = margin;
|
||||||
|
rect.bottom = getHeight() - margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawRect(rect, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onColorChanged(int color) {
|
||||||
|
if (onColorChanged != null) {
|
||||||
|
onColorChanged.onColorChanged(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
|
||||||
|
int actionId = event.getAction();
|
||||||
|
|
||||||
|
int newColor;
|
||||||
|
|
||||||
|
switch (actionId) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
isClick = true;
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
newColor = getColorAtXY(event.getX(), event.getY());
|
||||||
|
|
||||||
|
setSelectedColor(newColor);
|
||||||
|
|
||||||
|
if (isClick) {
|
||||||
|
performClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
newColor = getColorAtXY(event.getX(), event.getY());
|
||||||
|
|
||||||
|
setSelectedColor(newColor);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
isClick = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_OUTSIDE:
|
||||||
|
isClick = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return color at x,y coordinate of view.
|
||||||
|
*/
|
||||||
|
private int getColorAtXY(float x, float y) {
|
||||||
|
|
||||||
|
// FIXME: colors.length == 0 -> devision by ZERO.s
|
||||||
|
|
||||||
|
if (mOrientation == HORIZONTAL) {
|
||||||
|
int left = 0;
|
||||||
|
int right = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < colors.length; i++) {
|
||||||
|
left = right;
|
||||||
|
right += cellSize;
|
||||||
|
|
||||||
|
if (left <= x && right >= x) {
|
||||||
|
return colors[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
int top = 0;
|
||||||
|
int bottom = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < colors.length; i++) {
|
||||||
|
top = bottom;
|
||||||
|
bottom += cellSize;
|
||||||
|
|
||||||
|
if (y >= top && y <= bottom) {
|
||||||
|
return colors[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Parcelable onSaveInstanceState() {
|
||||||
|
// begin boilerplate code that allows parent classes to save state
|
||||||
|
Parcelable superState = super.onSaveInstanceState();
|
||||||
|
|
||||||
|
SavedState ss = new SavedState(superState);
|
||||||
|
// end
|
||||||
|
|
||||||
|
ss.selectedColor = this.selectedColor;
|
||||||
|
ss.isColorSelected = this.isColorSelected;
|
||||||
|
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Parcelable state) {
|
||||||
|
// begin boilerplate code so parent classes can restore state
|
||||||
|
if (!(state instanceof SavedState)) {
|
||||||
|
super.onRestoreInstanceState(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SavedState ss = (SavedState) state;
|
||||||
|
super.onRestoreInstanceState(ss.getSuperState());
|
||||||
|
// end
|
||||||
|
|
||||||
|
this.selectedColor = ss.selectedColor;
|
||||||
|
this.isColorSelected = ss.isColorSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean performClick() {
|
||||||
|
return super.performClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
|
||||||
|
screenW = w;
|
||||||
|
screenH = h;
|
||||||
|
|
||||||
|
recalcCellSize();
|
||||||
|
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return currently selected color.
|
||||||
|
*/
|
||||||
|
public int getColor() {
|
||||||
|
return selectedColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
// int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
// int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
// this.setMeasuredDimension(parentWidth, parentHeight);
|
||||||
|
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set selected color as color value from palette.
|
||||||
|
*/
|
||||||
|
public void setSelectedColor(int color) {
|
||||||
|
|
||||||
|
// not from current palette
|
||||||
|
if (!containsColor(colors, color)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do we need to re-draw view?
|
||||||
|
if (!isColorSelected || selectedColor != color) {
|
||||||
|
this.selectedColor = color;
|
||||||
|
|
||||||
|
isColorSelected = true;
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
onColorChanged(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set selected color as index from palete
|
||||||
|
*/
|
||||||
|
public void setSelectedColorPosition(int position) {
|
||||||
|
setSelectedColor(colors[position]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int recalcCellSize() {
|
||||||
|
|
||||||
|
if (mOrientation == HORIZONTAL) {
|
||||||
|
cellSize = Math.round(screenW / (colors.length * 1f));
|
||||||
|
} else {
|
||||||
|
cellSize = Math.round(screenH / (colors.length * 1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cellSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return current picker palete
|
||||||
|
*/
|
||||||
|
public int[] getColors() {
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set picker palette
|
||||||
|
*/
|
||||||
|
public void setColors(int[] colors) {
|
||||||
|
// TODO: selected color can be NOT in set of colors
|
||||||
|
// FIXME: colors can be null
|
||||||
|
this.colors = colors;
|
||||||
|
|
||||||
|
if (!containsColor(colors, selectedColor)) {
|
||||||
|
selectedColor = colors[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
recalcCellSize();
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if palette contains this color
|
||||||
|
*/
|
||||||
|
private boolean containsColor(int[] colors, int c) {
|
||||||
|
for (int i = 0; i < colors.length; i++) {
|
||||||
|
if (colors[i] == c)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set onColorChanged listener
|
||||||
|
*
|
||||||
|
* @param l
|
||||||
|
*/
|
||||||
|
public void setOnColorChangedListener(OnColorChangedListener l) {
|
||||||
|
this.onColorChanged = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SavedState extends BaseSavedState {
|
||||||
|
// required field that makes Parcelables from a Parcel
|
||||||
|
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
|
||||||
|
public SavedState createFromParcel(Parcel in) {
|
||||||
|
return new SavedState(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SavedState[] newArray(int size) {
|
||||||
|
return new SavedState[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
int selectedColor;
|
||||||
|
boolean isColorSelected;
|
||||||
|
|
||||||
|
SavedState(Parcelable superState) {
|
||||||
|
super(superState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SavedState(Parcel in) {
|
||||||
|
super(in);
|
||||||
|
this.selectedColor = in.readInt();
|
||||||
|
this.isColorSelected = in.readInt() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
|
super.writeToParcel(out, flags);
|
||||||
|
out.writeInt(this.selectedColor);
|
||||||
|
out.writeInt(this.isColorSelected ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package uz.shift.colorpicker;
|
||||||
|
|
||||||
|
public interface OnColorChangedListener {
|
||||||
|
void onColorChanged(int c);
|
||||||
|
}
|
24
app/src/main/java/uz/shift/colorpicker/Palette.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package uz.shift.colorpicker;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
|
public class Palette {
|
||||||
|
|
||||||
|
public static int[] DEFAULT;
|
||||||
|
|
||||||
|
static {
|
||||||
|
|
||||||
|
DEFAULT = new int[]{Color.parseColor("#b8c847"),
|
||||||
|
Color.parseColor("#67bb43"), Color.parseColor("#41b691"),
|
||||||
|
Color.parseColor("#4182b6"), Color.parseColor("#4149b6"),
|
||||||
|
Color.parseColor("#7641b6"), Color.parseColor("#b741a7"),
|
||||||
|
Color.parseColor("#c54657"), Color.parseColor("#d1694a"),
|
||||||
|
Color.parseColor("#d1904a"), Color.parseColor("#d1c54a")};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Palette() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
app/src/main/res/drawable-hdpi/ic_list_white_24dp.png
Normal file
After Width: | Height: | Size: 116 B |
BIN
app/src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
app/src/main/res/drawable-hdpi/ic_straighten_white_24dp.png
Normal file
After Width: | Height: | Size: 156 B |
BIN
app/src/main/res/drawable-mdpi/ic_list_white_24dp.png
Normal file
After Width: | Height: | Size: 86 B |
BIN
app/src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png
Normal file
After Width: | Height: | Size: 154 B |
BIN
app/src/main/res/drawable-mdpi/ic_straighten_white_24dp.png
Normal file
After Width: | Height: | Size: 120 B |
BIN
app/src/main/res/drawable-xhdpi/ic_list_white_24dp.png
Normal file
After Width: | Height: | Size: 95 B |
BIN
app/src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png
Normal file
After Width: | Height: | Size: 219 B |
BIN
app/src/main/res/drawable-xhdpi/ic_straighten_white_24dp.png
Normal file
After Width: | Height: | Size: 161 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_list_white_24dp.png
Normal file
After Width: | Height: | Size: 94 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png
Normal file
After Width: | Height: | Size: 269 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_straighten_white_24dp.png
Normal file
After Width: | Height: | Size: 223 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_list_white_24dp.png
Normal file
After Width: | Height: | Size: 100 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png
Normal file
After Width: | Height: | Size: 302 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_straighten_white_24dp.png
Normal file
After Width: | Height: | Size: 276 B |
4
app/src/main/res/layout/dialog_functions.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent" />
|
27
app/src/main/res/layout/dialog_functions_function.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/list_item_height"
|
||||||
|
android:background="@drawable/material_clickable_selector"
|
||||||
|
android:gravity="center_vertical|start"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
tools:ignore="Overdraw">
|
||||||
|
|
||||||
|
<org.solovyev.android.plotter.PlotIconView
|
||||||
|
android:id="@+id/function_icon"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fn_name_edittext"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:drawableEnd="@drawable/ic_mode_edit_white_18dp"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:drawableRight="@drawable/ic_mode_edit_white_18dp" />
|
||||||
|
</LinearLayout>
|
@ -31,51 +31,8 @@
|
|||||||
a:layout_height="wrap_content"
|
a:layout_height="wrap_content"
|
||||||
a:orientation="vertical">
|
a:orientation="vertical">
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
|
||||||
a:id="@+id/function_name_label"
|
|
||||||
a:layout_width="match_parent"
|
|
||||||
a:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<EditText
|
<include layout="@layout/fragment_function_edit_base_controls" />
|
||||||
a:id="@+id/function_name"
|
|
||||||
a:layout_width="match_parent"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
a:hint="@string/c_function_name"
|
|
||||||
a:inputType="text" />
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
|
|
||||||
<org.solovyev.android.calculator.functions.FunctionParamsView
|
|
||||||
a:id="@+id/function_params"
|
|
||||||
a:layout_width="match_parent"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
a:orientation="vertical" />
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
|
||||||
a:id="@+id/function_body_label"
|
|
||||||
a:layout_width="match_parent"
|
|
||||||
a:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<org.solovyev.android.calculator.view.EditTextCompat
|
|
||||||
a:id="@+id/function_body"
|
|
||||||
a:layout_width="match_parent"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
a:hint="@string/c_function_value"
|
|
||||||
a:imeOptions="flagNoExtractUi"
|
|
||||||
a:inputType="text" />
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
|
||||||
a:layout_width="match_parent"
|
|
||||||
a:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
a:id="@+id/function_description"
|
|
||||||
a:layout_width="match_parent"
|
|
||||||
a:layout_height="wrap_content"
|
|
||||||
a:hint="@string/c_function_description"
|
|
||||||
a:inputType="textMultiLine"
|
|
||||||
a:maxLines="4" />
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
~ Copyright 2013 serso aka se.solovyev
|
||||||
|
~
|
||||||
|
~ 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.
|
||||||
|
~
|
||||||
|
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~ Contact details
|
||||||
|
~
|
||||||
|
~ Email: se.solovyev@gmail.com
|
||||||
|
~ Site: http://se.solovyev.org
|
||||||
|
-->
|
||||||
|
|
||||||
|
<merge xmlns:a="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
a:id="@+id/function_name_label"
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
a:id="@+id/function_name"
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:hint="@string/c_function_name"
|
||||||
|
a:inputType="text" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<org.solovyev.android.calculator.functions.FunctionParamsView
|
||||||
|
a:id="@+id/function_params"
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:orientation="vertical" />
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
a:id="@+id/function_body_label"
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<org.solovyev.android.calculator.view.EditTextCompat
|
||||||
|
a:id="@+id/function_body"
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:hint="@string/c_function_value"
|
||||||
|
a:imeOptions="flagNoExtractUi"
|
||||||
|
a:inputType="text" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
a:id="@+id/function_description"
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:hint="@string/c_function_description"
|
||||||
|
a:inputType="textMultiLine"
|
||||||
|
a:maxLines="4" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
</merge>
|
||||||
|
|
64
app/src/main/res/layout/fragment_plot.xml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<org.solovyev.android.plotter.PlotViewFrame xmlns:a="http://schemas.android.com/apk/res/android"
|
||||||
|
a:id="@id/plot_view_frame"
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="match_parent">
|
||||||
|
|
||||||
|
<org.solovyev.android.plotter.PlotView
|
||||||
|
a:id="@id/plot_view"
|
||||||
|
a:layout_width="match_parent"
|
||||||
|
a:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<org.solovyev.android.material.MaterialImageButton
|
||||||
|
a:id="@+id/plot_dimensions"
|
||||||
|
style="@style/CppPlotButton"
|
||||||
|
a:layout_gravity="top|start"
|
||||||
|
a:src="@drawable/ic_straighten_white_24dp" />
|
||||||
|
|
||||||
|
<org.solovyev.android.material.MaterialButton
|
||||||
|
a:id="@id/plot_3d_button"
|
||||||
|
style="@style/CppPlotButton"
|
||||||
|
a:layout_gravity="top|end"
|
||||||
|
a:text="@string/cpp_plot_3d" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
a:layout_width="wrap_content"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:layout_gravity="bottom|start"
|
||||||
|
a:orientation="vertical">
|
||||||
|
|
||||||
|
<org.solovyev.android.material.MaterialButton
|
||||||
|
a:id="@+id/plot_add_function"
|
||||||
|
style="@style/CppPlotButton"
|
||||||
|
a:text="@string/cpp_plot_add_function" />
|
||||||
|
|
||||||
|
<org.solovyev.android.material.MaterialImageButton
|
||||||
|
a:id="@+id/plot_functions"
|
||||||
|
style="@style/CppPlotButton"
|
||||||
|
a:src="@drawable/ic_list_white_24dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
a:layout_width="wrap_content"
|
||||||
|
a:layout_height="wrap_content"
|
||||||
|
a:layout_gravity="bottom|end"
|
||||||
|
a:orientation="vertical">
|
||||||
|
|
||||||
|
<org.solovyev.android.material.MaterialButton
|
||||||
|
a:id="@id/plot_zoom_in_button"
|
||||||
|
style="@style/CppPlotButton"
|
||||||
|
a:text="@string/cpp_plot_zoom_in" />
|
||||||
|
|
||||||
|
<org.solovyev.android.material.MaterialButton
|
||||||
|
a:id="@id/plot_zoom_reset_button"
|
||||||
|
style="@style/CppPlotButton"
|
||||||
|
a:text="@string/cpp_plot_zoom_reset" />
|
||||||
|
|
||||||
|
<org.solovyev.android.material.MaterialButton
|
||||||
|
a:id="@id/plot_zoom_out_button"
|
||||||
|
style="@style/CppPlotButton"
|
||||||
|
a:text="@string/cpp_plot_zoom_out" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</org.solovyev.android.plotter.PlotViewFrame>
|
68
app/src/main/res/layout/fragment_plot_dimensions.xml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/x_bounds"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/plot_x_min_label"
|
||||||
|
style="@style/DimensionTextInput">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/plot_x_min"
|
||||||
|
style="@style/DimensionEditText"
|
||||||
|
android:hint="@string/cpp_dimensions_x_min"
|
||||||
|
android:nextFocusRight="@+id/plot_x_max" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/plot_x_max_label"
|
||||||
|
style="@style/DimensionTextInput"
|
||||||
|
android:layout_marginLeft="@dimen/form_input_margin_hor"
|
||||||
|
android:layout_marginStart="@dimen/form_input_margin_hor">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/plot_x_max"
|
||||||
|
style="@style/DimensionEditText"
|
||||||
|
android:hint="@string/dimensions_x_max"
|
||||||
|
android:nextFocusDown="@+id/plot_y_min"
|
||||||
|
android:nextFocusRight="@+id/plot_y_min" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/y_bounds"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/plot_y_min_label"
|
||||||
|
style="@style/DimensionTextInput">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/plot_y_min"
|
||||||
|
style="@style/DimensionEditText"
|
||||||
|
android:hint="@string/dimensions_y_min"
|
||||||
|
android:nextFocusRight="@+id/plot_y_max" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/plot_y_max_label"
|
||||||
|
style="@style/DimensionTextInput"
|
||||||
|
android:layout_marginLeft="@dimen/form_input_margin_hor"
|
||||||
|
android:layout_marginStart="@dimen/form_input_margin_hor">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/plot_y_max"
|
||||||
|
style="@style/DimensionEditText.Last"
|
||||||
|
android:hint="@string/dimensions_y_max" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
90
app/src/main/res/layout/fragment_plot_function_edit.xml
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
~ Copyright 2013 serso aka se.solovyev
|
||||||
|
~
|
||||||
|
~ 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.
|
||||||
|
~
|
||||||
|
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~ Contact details
|
||||||
|
~
|
||||||
|
~ Email: se.solovyev@gmail.com
|
||||||
|
~ Site: http://se.solovyev.org
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
|
<include layout="@layout/fragment_function_edit_base_controls" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/fn_meshspec_views"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<org.solovyev.android.plotter.PlotIconView
|
||||||
|
android:id="@+id/fn_iconview"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:background="#212121"
|
||||||
|
android:padding="5dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fn_linewidth_label"
|
||||||
|
style="@style/TextAppearance.AppCompat.Caption"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/fn_line_width" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/fn_linewidth_seekbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fn_color_label"
|
||||||
|
style="@style/TextAppearance.AppCompat.Caption"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/fn_line_color" />
|
||||||
|
|
||||||
|
<uz.shift.colorpicker.LineColorPicker
|
||||||
|
android:id="@+id/fn_color_picker"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
app:lcp_orientation="horizontal" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
@ -37,12 +37,6 @@
|
|||||||
a:title="@string/cpp_plot_3d"
|
a:title="@string/cpp_plot_3d"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
|
||||||
a:id="@+id/menu_plot_fullscreen"
|
|
||||||
a:icon="@drawable/ab_expand"
|
|
||||||
a:title="@string/cpp_fullscreen"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
a:id="@+id/menu_plot_range"
|
a:id="@+id/menu_plot_range"
|
||||||
a:icon="@drawable/ab_range"
|
a:icon="@drawable/ab_range"
|
||||||
|
13
app/src/main/res/values/attrs_lcp.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<declare-styleable name="LineColorPicker">
|
||||||
|
<attr name="lcp_orientation" format="enum">
|
||||||
|
<enum name="horizontal" value="0" />
|
||||||
|
<enum name="vertical" value="1" />
|
||||||
|
</attr>
|
||||||
|
<attr name="lcp_colors" format="reference" />
|
||||||
|
<attr name="lcp_selectedColorIndex" format="integer" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
</resources>
|
@ -42,4 +42,6 @@
|
|||||||
<dimen name="cpp_image_button_padding">5dp</dimen>
|
<dimen name="cpp_image_button_padding">5dp</dimen>
|
||||||
<dimen name="cpp_dialog_width_max">400dp</dimen>
|
<dimen name="cpp_dialog_width_max">400dp</dimen>
|
||||||
<dimen name="cpp_card_margin">4dp</dimen>
|
<dimen name="cpp_card_margin">4dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="list_item_height">48dp</dimen>
|
||||||
</resources>
|
</resources>
|
@ -167,14 +167,6 @@
|
|||||||
<item name="android:orientation">horizontal</item>
|
<item name="android:orientation">horizontal</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="WizardButton" parent="MaterialButton">
|
|
||||||
<item name="android:singleLine">true</item>
|
|
||||||
<item name="android:maxLines">1</item>
|
|
||||||
<item name="android:lines">1</item>
|
|
||||||
<item name="android:ellipsize">end</item>
|
|
||||||
<item name="android:textAppearance">@android:style/TextAppearance.Medium</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="WizardPrimaryButton" parent="PrimaryButton">
|
<style name="WizardPrimaryButton" parent="PrimaryButton">
|
||||||
<item name="android:singleLine">true</item>
|
<item name="android:singleLine">true</item>
|
||||||
<item name="android:maxLines">1</item>
|
<item name="android:maxLines">1</item>
|
||||||
@ -202,6 +194,40 @@
|
|||||||
<item name="android:textAppearance">@android:style/TextAppearance.Medium</item>
|
<item name="android:textAppearance">@android:style/TextAppearance.Medium</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="CppPlotButton">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:textColor">@color/cpp_button_text</item>
|
||||||
|
<item name="materialColor">@color/cpp_wizard_button_selector</item>
|
||||||
|
<item name="android:textAppearance">@android:style/TextAppearance.Medium</item>
|
||||||
|
<item name="android:minWidth">40dp</item>
|
||||||
|
<item name="android:minHeight">40dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="DimensionEditText.Last" parent="DimensionEditText">
|
||||||
|
<item name="android:imeOptions">actionDone|flagNoExtractUi</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="DimensionEditText">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:imeOptions">actionNext|flagNoExtractUi</item>
|
||||||
|
<item name="android:inputType">numberDecimal|numberSigned</item>
|
||||||
|
<item name="android:selectAllOnFocus">true</item>
|
||||||
|
<item name="android:gravity">start|center_vertical</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="DimensionTextInput">
|
||||||
|
<item name="android:layout_width">100dp</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="DimensionLabel">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:textAppearance">@android:style/TextAppearance.Medium</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="CppPane.Multipane.Left" parent="CppPane.Multipane">
|
<style name="CppPane.Multipane.Left" parent="CppPane.Multipane">
|
||||||
<item name="android:layout_marginLeft">0dp</item>
|
<item name="android:layout_marginLeft">0dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -6,4 +6,12 @@
|
|||||||
<string name="cpp_kb_undo" translatable="false">↶</string>
|
<string name="cpp_kb_undo" translatable="false">↶</string>
|
||||||
<string name="cpp_kb_redo" translatable="false">↷</string>
|
<string name="cpp_kb_redo" translatable="false">↷</string>
|
||||||
<string name="cpp_direction_text_size" translatable="false">0.3;0.3;0.3;0.25</string>
|
<string name="cpp_direction_text_size" translatable="false">0.3;0.3;0.3;0.25</string>
|
||||||
|
<string name="cpp_plot_add_function" translatable="false">+</string>
|
||||||
|
<string name="cpp_plot_zoom_in" translatable="false">+</string>
|
||||||
|
<string name="cpp_plot_zoom_reset" translatable="false">0</string>
|
||||||
|
<string name="cpp_plot_zoom_out" translatable="false">−</string>
|
||||||
|
<string name="cpp_dimensions_x_min" translatable="false">X min</string>
|
||||||
|
<string name="dimensions_x_max" translatable="false">X max</string>
|
||||||
|
<string name="dimensions_y_min" translatable="false">Y min</string>
|
||||||
|
<string name="dimensions_y_max" translatable="false">Y max</string>
|
||||||
</resources>
|
</resources>
|
@ -239,4 +239,6 @@
|
|||||||
<string name="cpp_system_language">System language</string>
|
<string name="cpp_system_language">System language</string>
|
||||||
<string name="cpp_missing_permission_title">Missing permission</string>
|
<string name="cpp_missing_permission_title">Missing permission</string>
|
||||||
<string name="cpp_missing_permission_msg">Please enable \"%1$s\" permission in system settings</string>
|
<string name="cpp_missing_permission_msg">Please enable \"%1$s\" permission in system settings</string>
|
||||||
|
<string name="fn_line_width">Line width</string>
|
||||||
|
<string name="fn_line_color">Line color</string>
|
||||||
</resources>
|
</resources>
|
||||||
|