Project restructure
We don't need to separate widget/onscreen from the main module, let's merge them together.
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.text.Html;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import org.solovyev.android.calculator.text.TextProcessor;
|
||||
import org.solovyev.android.calculator.text.TextProcessorEditorResult;
|
||||
import org.solovyev.android.calculator.view.TextHighlighter;
|
||||
import org.solovyev.android.view.AutoResizeTextView;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/17/11
|
||||
* Time: 10:58 PM
|
||||
*/
|
||||
public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView {
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* STATIC FIELDS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
@Nonnull
|
||||
private final static TextProcessor<TextProcessorEditorResult, String> textHighlighter = new TextHighlighter(Color.WHITE, false);
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* FIELDS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
@Nonnull
|
||||
private volatile CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
|
||||
|
||||
private volatile boolean viewStateChange = false;
|
||||
|
||||
@Nonnull
|
||||
private final Object lock = new Object();
|
||||
|
||||
@Nonnull
|
||||
private final Handler uiHandler = new Handler();
|
||||
|
||||
@Nonnull
|
||||
private final ExecutorService bgExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private volatile boolean initialized = false;
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* CONSTRUCTORS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
}
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* METHODS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
|
||||
@Override
|
||||
public void setState(@Nonnull final CalculatorDisplayViewState state) {
|
||||
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
synchronized (lock) {
|
||||
try {
|
||||
viewStateChange = true;
|
||||
|
||||
final CharSequence text = prepareText(state.getStringResult(), state.isValid());
|
||||
|
||||
AndroidCalculatorDisplayView.this.state = state;
|
||||
if (state.isValid()) {
|
||||
setTextColor(getResources().getColor(R.color.cpp_default_text_color));
|
||||
setText(text);
|
||||
|
||||
adjustTextSize();
|
||||
|
||||
} else {
|
||||
// update text in order to get rid of HTML tags
|
||||
setText(getText().toString());
|
||||
setTextColor(getResources().getColor(R.color.cpp_display_error_text_color));
|
||||
|
||||
// error messages are never shown -> just greyed out text (error message will be shown on click)
|
||||
//setText(state.getErrorMessage());
|
||||
//redraw();
|
||||
}
|
||||
} finally {
|
||||
viewStateChange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CalculatorDisplayViewState getState() {
|
||||
synchronized (lock) {
|
||||
return this.state;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CharSequence prepareText(@Nullable String text, boolean valid) {
|
||||
CharSequence result;
|
||||
|
||||
if (valid && text != null) {
|
||||
|
||||
//Log.d(this.getClass().getName(), text);
|
||||
|
||||
try {
|
||||
final TextProcessorEditorResult processedText = textHighlighter.process(text);
|
||||
text = processedText.toString();
|
||||
result = Html.fromHtml(text);
|
||||
} catch (CalculatorParseException e) {
|
||||
result = text;
|
||||
}
|
||||
} else {
|
||||
result = text;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void adjustTextSize() {
|
||||
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
|
||||
setAddEllipsis(false);
|
||||
setMinTextSize(10);
|
||||
resizeText();
|
||||
}
|
||||
|
||||
public synchronized void init(@Nonnull Context context) {
|
||||
this.init(context, true);
|
||||
}
|
||||
|
||||
public synchronized void init(@Nonnull Context context, boolean fromApp) {
|
||||
if (!initialized) {
|
||||
if (fromApp) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final CalculatorPreferences.Gui.Layout layout = CalculatorPreferences.Gui.getLayout(preferences);
|
||||
|
||||
if (!layout.isOptimized()) {
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, getResources().getDimension(R.dimen.cpp_display_text_size_mobile));
|
||||
}
|
||||
|
||||
if (context instanceof FragmentActivity) {
|
||||
this.setOnClickListener(new CalculatorDisplayOnClickListener((FragmentActivity) context));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Must be fragment activity, got " + context.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ContextMenu;
|
||||
import android.widget.EditText;
|
||||
import org.solovyev.common.collections.Collections;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/17/11
|
||||
* Time: 12:25 AM
|
||||
*/
|
||||
public class AndroidCalculatorEditorView extends EditText implements CalculatorEditorView {
|
||||
|
||||
private volatile boolean initialized = false;
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
@Nonnull
|
||||
private volatile CalculatorEditorViewState viewState = CalculatorEditorViewStateImpl.newDefaultInstance();
|
||||
|
||||
private volatile boolean viewStateChange = false;
|
||||
|
||||
@Nonnull
|
||||
private final Handler uiHandler = new Handler();
|
||||
|
||||
public AndroidCalculatorEditorView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AndroidCalculatorEditorView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public AndroidCalculatorEditorView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor() {
|
||||
// NOTE: code below can be used carefully and should not be copied without special intention
|
||||
// The main purpose of code is to disable soft input (virtual keyboard) but leave all the TextEdit functionality, like cursor, scrolling, copy/paste menu etc
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
// fix for missing cursor in android 3 and higher
|
||||
try {
|
||||
// IDEA: return false always except if method was called from TextView.isCursorVisible() method
|
||||
for (StackTraceElement stackTraceElement : Collections.asList(Thread.currentThread().getStackTrace())) {
|
||||
if ("isCursorVisible".equals(stackTraceElement.getMethodName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
// just in case...
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreateContextMenu(ContextMenu menu) {
|
||||
super.onCreateContextMenu(menu);
|
||||
|
||||
menu.removeItem(android.R.id.selectAll);
|
||||
}
|
||||
|
||||
public void setHighlightText(boolean highlightText) {
|
||||
//this.highlightText = highlightText;
|
||||
Locator.getInstance().getEditor().updateViewState();
|
||||
}
|
||||
|
||||
public synchronized void init() {
|
||||
if (!initialized) {
|
||||
this.addTextChangedListener(new TextWatcherImpl());
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(@Nonnull final CalculatorEditorViewState viewState) {
|
||||
synchronized (this) {
|
||||
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final AndroidCalculatorEditorView editorView = AndroidCalculatorEditorView.this;
|
||||
synchronized (AndroidCalculatorEditorView.this) {
|
||||
try {
|
||||
editorView.viewStateChange = true;
|
||||
editorView.viewState = viewState;
|
||||
editorView.setText(viewState.getTextAsCharSequence(), BufferType.EDITABLE);
|
||||
final int selection = CalculatorEditorImpl.correctSelection(viewState.getSelection(), editorView.getText());
|
||||
editorView.setSelection(selection);
|
||||
} finally {
|
||||
editorView.viewStateChange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSelectionChanged(int selStart, int selEnd) {
|
||||
synchronized (this) {
|
||||
if (initialized && !viewStateChange) {
|
||||
// external text change => need to notify editor
|
||||
super.onSelectionChanged(selStart, selEnd);
|
||||
|
||||
if (selStart == selEnd) {
|
||||
// only if cursor moving, if selection do nothing
|
||||
Locator.getInstance().getEditor().setSelection(selStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleTextChange(Editable s) {
|
||||
synchronized (this) {
|
||||
if (initialized && !viewStateChange) {
|
||||
// external text change => need to notify editor
|
||||
Locator.getInstance().getEditor().setText(String.valueOf(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class TextWatcherImpl implements TextWatcher {
|
||||
|
||||
@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) {
|
||||
handleTextChange(s);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.app.Application;
|
||||
import org.solovyev.android.UiThreadExecutor;
|
||||
import org.solovyev.common.listeners.JEvent;
|
||||
import org.solovyev.common.listeners.JEventListener;
|
||||
import org.solovyev.common.listeners.JEventListeners;
|
||||
import org.solovyev.common.listeners.Listeners;
|
||||
import org.solovyev.common.threads.DelayedExecutor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 12/1/12
|
||||
* Time: 3:58 PM
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class aggregates several useful in any Android application interfaces and provides access to {@link android.app.Application} object from a static context.
|
||||
* NOTE: use this class only if you don't use and dependency injection library (if you use any you can directly set interfaces through it). <br/>
|
||||
* <p/>
|
||||
* Before first usage this class must be initialized by calling {@link App#init(android.app.Application)} method (for example, from {@link android.app.Application#onCreate()})
|
||||
*/
|
||||
public final class App {
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* FIELDS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
@Nonnull
|
||||
private static volatile Application application;
|
||||
|
||||
@Nonnull
|
||||
private static volatile ServiceLocator locator;
|
||||
|
||||
@Nonnull
|
||||
private static volatile DelayedExecutor uiThreadExecutor;
|
||||
|
||||
@Nonnull
|
||||
private static volatile JEventListeners<JEventListener<? extends JEvent>, JEvent> eventBus;
|
||||
|
||||
private static volatile boolean initialized;
|
||||
|
||||
@Nonnull
|
||||
private static CalculatorBroadcaster broadcaster;
|
||||
|
||||
private App() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* METHODS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public static <A extends Application & ServiceLocator> void init(@Nonnull A application) {
|
||||
init(application, new UiThreadExecutor(), Listeners.newEventBus(), application);
|
||||
}
|
||||
|
||||
public static void init(@Nonnull Application application, @Nullable ServiceLocator serviceLocator) {
|
||||
init(application, new UiThreadExecutor(), Listeners.newEventBus(), serviceLocator);
|
||||
}
|
||||
|
||||
public static void init(@Nonnull Application application,
|
||||
@Nonnull UiThreadExecutor uiThreadExecutor,
|
||||
@Nonnull JEventListeners<JEventListener<? extends JEvent>, JEvent> eventBus,
|
||||
@Nullable ServiceLocator serviceLocator) {
|
||||
if (!initialized) {
|
||||
App.application = application;
|
||||
App.uiThreadExecutor = uiThreadExecutor;
|
||||
App.eventBus = eventBus;
|
||||
if (serviceLocator != null) {
|
||||
App.locator = serviceLocator;
|
||||
} else {
|
||||
// empty service locator
|
||||
App.locator = new ServiceLocator() {
|
||||
};
|
||||
}
|
||||
App.broadcaster = new CalculatorBroadcaster(application);
|
||||
|
||||
App.initialized = true;
|
||||
} else {
|
||||
throw new IllegalStateException("Already initialized!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkInit() {
|
||||
if (!initialized) {
|
||||
throw new IllegalStateException("App should be initialized!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if App has already been initialized, false otherwise
|
||||
*/
|
||||
public static boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <A> real type of application
|
||||
* @return application instance which was provided in {@link App#init(android.app.Application)} method
|
||||
*/
|
||||
@Nonnull
|
||||
public static <A extends Application> A getApplication() {
|
||||
checkInit();
|
||||
return (A) application;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <L> real type of service locator
|
||||
* @return instance of service locator user in application
|
||||
*/
|
||||
@Nonnull
|
||||
public static <L extends ServiceLocator> L getLocator() {
|
||||
checkInit();
|
||||
return (L) locator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns executor which runs on Main Application's thread. It's safe to do all UI work on this executor
|
||||
*
|
||||
* @return UI thread executor
|
||||
*/
|
||||
@Nonnull
|
||||
public static DelayedExecutor getUiThreadExecutor() {
|
||||
checkInit();
|
||||
return uiThreadExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return application's event bus
|
||||
*/
|
||||
@Nonnull
|
||||
public static JEventListeners<JEventListener<? extends JEvent>, JEvent> getEventBus() {
|
||||
checkInit();
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static CalculatorBroadcaster getBroadcaster() {
|
||||
return broadcaster;
|
||||
}
|
||||
}
|
@@ -215,6 +215,9 @@ public class CalculatorApplication extends android.app.Application implements Sh
|
||||
Locator.getInstance().getNotifier().showDebugMessage(TAG, "Application started!");
|
||||
|
||||
typeFace = Typeface.createFromAsset(getAssets(), "fonts/Roboto-Regular.ttf");
|
||||
|
||||
// we must update the widget when app starts
|
||||
App.getBroadcaster().sendEditorStateChangedIntent();
|
||||
}
|
||||
|
||||
private void setTheme(@Nonnull SharedPreferences preferences) {
|
||||
|
@@ -0,0 +1,46 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class CalculatorBroadcaster implements CalculatorEventListener {
|
||||
|
||||
public static final String ACTION_INIT = "org.solovyev.android.calculator.INIT";
|
||||
public static final String ACTION_EDITOR_STATE_CHANGED = "org.solovyev.android.calculator.EDITOR_STATE_CHANGED";
|
||||
public static final String ACTION_DISPLAY_STATE_CHANGED = "org.solovyev.android.calculator.DISPLAY_STATE_CHANGED";
|
||||
|
||||
@Nonnull
|
||||
private final Context context;
|
||||
|
||||
public CalculatorBroadcaster(@Nonnull Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCalculatorEvent(@Nonnull CalculatorEventData calculatorEventData, @Nonnull CalculatorEventType calculatorEventType, @Nullable Object data) {
|
||||
switch (calculatorEventType) {
|
||||
case editor_state_changed:
|
||||
case editor_state_changed_light:
|
||||
sendEditorStateChangedIntent();
|
||||
break;
|
||||
case display_state_changed:
|
||||
sendDisplayStateChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendDisplayStateChanged() {
|
||||
sendBroadcastIntent(ACTION_DISPLAY_STATE_CHANGED);
|
||||
}
|
||||
|
||||
public void sendEditorStateChangedIntent() {
|
||||
sendBroadcastIntent(ACTION_EDITOR_STATE_CHANGED);
|
||||
}
|
||||
|
||||
public void sendBroadcastIntent(@Nonnull String action) {
|
||||
context.sendBroadcast(new Intent(action));
|
||||
}
|
||||
}
|
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.solovyev.android.calculator.R;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.cursor_left;
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.cursor_right;
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.functions_detached;
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.history_detached;
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.like;
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.open_app;
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.operators_detached;
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.settings_detached;
|
||||
import static org.solovyev.android.calculator.CalculatorSpecialButton.vars_detached;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 10/20/12
|
||||
* Time: 12:05 AM
|
||||
*/
|
||||
public enum CalculatorButton {
|
||||
|
||||
/*digits*/
|
||||
one(R.id.cpp_button_1, "1"),
|
||||
two(R.id.cpp_button_2, "2"),
|
||||
three(R.id.cpp_button_3, "3"),
|
||||
four(R.id.cpp_button_4, "4"),
|
||||
five(R.id.cpp_button_5, "5"),
|
||||
six(R.id.cpp_button_6, "6"),
|
||||
seven(R.id.cpp_button_7, "7"),
|
||||
eight(R.id.cpp_button_8, "8"),
|
||||
nine(R.id.cpp_button_9, "9"),
|
||||
zero(R.id.cpp_button_0, "0"),
|
||||
|
||||
period(R.id.cpp_button_period, "."),
|
||||
brackets(R.id.cpp_button_round_brackets, "()"),
|
||||
|
||||
settings(R.id.cpp_button_settings, settings_detached),
|
||||
like(R.id.cpp_button_like, CalculatorSpecialButton.like),
|
||||
|
||||
/*last row*/
|
||||
left(R.id.cpp_button_left, cursor_left),
|
||||
right(R.id.cpp_button_right, cursor_right),
|
||||
vars(R.id.cpp_button_vars, vars_detached),
|
||||
functions(R.id.cpp_button_functions, functions_detached),
|
||||
operators(R.id.cpp_button_operators, operators_detached),
|
||||
app(R.id.cpp_button_app, open_app),
|
||||
history(R.id.cpp_button_history, history_detached),
|
||||
|
||||
/*operations*/
|
||||
multiplication(R.id.cpp_button_multiplication, "*"),
|
||||
division(R.id.cpp_button_division, "/"),
|
||||
plus(R.id.cpp_button_plus, "+"),
|
||||
subtraction(R.id.cpp_button_subtraction, "-"),
|
||||
percent(R.id.cpp_button_percent, "%"),
|
||||
power(R.id.cpp_button_power, "^"),
|
||||
|
||||
/*last column*/
|
||||
clear(R.id.cpp_button_clear, CalculatorSpecialButton.clear),
|
||||
erase(R.id.cpp_button_erase, CalculatorSpecialButton.erase, CalculatorSpecialButton.clear),
|
||||
copy(R.id.cpp_button_copy, CalculatorSpecialButton.copy),
|
||||
paste(R.id.cpp_button_paste, CalculatorSpecialButton.paste),
|
||||
|
||||
/*equals*/
|
||||
equals(R.id.cpp_button_equals, CalculatorSpecialButton.equals);
|
||||
|
||||
|
||||
private final int buttonId;
|
||||
|
||||
@Nonnull
|
||||
private final String onClickText;
|
||||
|
||||
@Nullable
|
||||
private final String onLongClickText;
|
||||
|
||||
@Nonnull
|
||||
private static Map<Integer, CalculatorButton> buttonsByIds = new HashMap<Integer, CalculatorButton>();
|
||||
|
||||
CalculatorButton(int buttonId, @Nonnull CalculatorSpecialButton onClickButton, @Nullable CalculatorSpecialButton onLongClickButton) {
|
||||
this(buttonId, onClickButton.getActionCode(), onLongClickButton == null ? null : onLongClickButton.getActionCode());
|
||||
}
|
||||
|
||||
CalculatorButton(int buttonId, @Nonnull CalculatorSpecialButton onClickButton) {
|
||||
this(buttonId, onClickButton, null);
|
||||
}
|
||||
|
||||
CalculatorButton(int buttonId, @Nonnull String onClickText, @Nullable String onLongClickText) {
|
||||
this.buttonId = buttonId;
|
||||
this.onClickText = onClickText;
|
||||
this.onLongClickText = onLongClickText;
|
||||
|
||||
}
|
||||
|
||||
CalculatorButton(int buttonId, @Nonnull String onClickText) {
|
||||
this(buttonId, onClickText, null);
|
||||
}
|
||||
|
||||
public void onLongClick(@Nonnull Context context) {
|
||||
Locator.getInstance().getNotifier().showDebugMessage("Calculator++ Widget", "Button pressed: " + onLongClickText);
|
||||
if (onLongClickText != null) {
|
||||
Locator.getInstance().getKeyboard().buttonPressed(onLongClickText);
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(@Nonnull Context context) {
|
||||
Locator.getInstance().getNotifier().showDebugMessage("Calculator++ Widget", "Button pressed: " + onClickText);
|
||||
Locator.getInstance().getKeyboard().buttonPressed(onClickText);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CalculatorButton getById(int buttonId) {
|
||||
initButtonsByIdsMap();
|
||||
|
||||
return buttonsByIds.get(buttonId);
|
||||
}
|
||||
|
||||
private static void initButtonsByIdsMap() {
|
||||
if (buttonsByIds.isEmpty()) {
|
||||
// if not initialized
|
||||
|
||||
final CalculatorButton[] calculatorButtons = values();
|
||||
|
||||
final Map<Integer, CalculatorButton> localButtonsByIds = new HashMap<Integer, CalculatorButton>(calculatorButtons.length);
|
||||
for (CalculatorButton calculatorButton : calculatorButtons) {
|
||||
localButtonsByIds.put(calculatorButton.getButtonId(), calculatorButton);
|
||||
}
|
||||
|
||||
buttonsByIds = localButtonsByIds;
|
||||
}
|
||||
}
|
||||
|
||||
public int getButtonId() {
|
||||
return buttonId;
|
||||
}
|
||||
}
|
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.RemoteViews;
|
||||
import jscl.AngleUnit;
|
||||
import jscl.NumeralBase;
|
||||
|
||||
import org.solovyev.android.Views;
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
|
||||
import org.solovyev.android.calculator.view.AngleUnitsButton;
|
||||
import org.solovyev.android.calculator.view.NumeralBasesButton;
|
||||
import org.solovyev.android.view.drag.DragButton;
|
||||
import org.solovyev.android.view.drag.DragDirection;
|
||||
import org.solovyev.android.view.drag.SimpleOnDragListener;
|
||||
import org.solovyev.common.math.Point2d;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/28/12
|
||||
* Time: 12:06 AM
|
||||
*/
|
||||
public final class CalculatorButtons {
|
||||
|
||||
private CalculatorButtons() {
|
||||
}
|
||||
|
||||
|
||||
public static void processButtons(@Nonnull CalculatorPreferences.Gui.Theme theme,
|
||||
@Nonnull CalculatorPreferences.Gui.Layout layout,
|
||||
@Nonnull View root) {
|
||||
if (!layout.isOptimized()) {
|
||||
|
||||
final float textSize = root.getContext().getResources().getDimension(R.dimen.cpp_keyboard_button_text_size_mobile);
|
||||
|
||||
Views.processViewsOfType(root, DragButton.class, new Views.ViewProcessor<DragButton>() {
|
||||
@Override
|
||||
public void process(@Nonnull DragButton button) {
|
||||
button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void initMultiplicationButton(@Nonnull View root) {
|
||||
final View multiplicationButton = root.findViewById(R.id.cpp_button_multiplication);
|
||||
if (multiplicationButton instanceof Button) {
|
||||
((Button) multiplicationButton).setText(Locator.getInstance().getEngine().getMultiplicationSign());
|
||||
}
|
||||
}
|
||||
|
||||
public static void initMultiplicationButton(@Nonnull RemoteViews views) {
|
||||
views.setTextViewText(R.id.cpp_button_multiplication, Locator.getInstance().getEngine().getMultiplicationSign());
|
||||
}
|
||||
|
||||
|
||||
public static void toggleEqualsButton(@Nullable SharedPreferences preferences,
|
||||
@Nonnull Activity activity) {
|
||||
preferences = preferences == null ? PreferenceManager.getDefaultSharedPreferences(activity) : preferences;
|
||||
|
||||
final boolean large = Views.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE, activity.getResources().getConfiguration()) &&
|
||||
CalculatorPreferences.Gui.getLayout(preferences).isOptimized();
|
||||
|
||||
if (!large) {
|
||||
if (Views.getScreenOrientation(activity) == Configuration.ORIENTATION_PORTRAIT
|
||||
|| !CalculatorPreferences.Gui.autoOrientation.getPreference(preferences)) {
|
||||
|
||||
final DragButton equalsButton = (DragButton) activity.findViewById(R.id.cpp_button_equals);
|
||||
if (equalsButton != null) {
|
||||
if (CalculatorPreferences.Gui.showEqualsButton.getPreference(preferences)) {
|
||||
equalsButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
equalsButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* STATIC CLASSES
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
static class RoundBracketsDragProcessor implements SimpleOnDragListener.DragProcessor {
|
||||
@Override
|
||||
public boolean processDragEvent(@Nonnull DragDirection dragDirection, @Nonnull DragButton dragButton, @Nonnull Point2d startPoint2d, @Nonnull MotionEvent motionEvent) {
|
||||
final boolean result;
|
||||
|
||||
if (dragDirection == DragDirection.left) {
|
||||
getKeyboard().roundBracketsButtonPressed();
|
||||
result = true;
|
||||
} else {
|
||||
result = new DigitButtonDragProcessor(getKeyboard()).processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static CalculatorKeyboard getKeyboard() {
|
||||
return Locator.getInstance().getKeyboard();
|
||||
}
|
||||
|
||||
static class VarsDragProcessor implements SimpleOnDragListener.DragProcessor {
|
||||
|
||||
@Nonnull
|
||||
private Context context;
|
||||
|
||||
VarsDragProcessor(@Nonnull Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDragEvent(@Nonnull DragDirection dragDirection,
|
||||
@Nonnull DragButton dragButton,
|
||||
@Nonnull Point2d startPoint2d,
|
||||
@Nonnull MotionEvent motionEvent) {
|
||||
boolean result = false;
|
||||
|
||||
if (dragDirection == DragDirection.up) {
|
||||
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_create_var_dialog, null, context);
|
||||
result = true;
|
||||
}/* else if (dragDirection == DragDirection.down) {
|
||||
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_create_matrix_dialog, null, context);
|
||||
result = true;
|
||||
}*/
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static class AngleUnitsChanger implements SimpleOnDragListener.DragProcessor {
|
||||
|
||||
@Nonnull
|
||||
private final DigitButtonDragProcessor processor;
|
||||
|
||||
@Nonnull
|
||||
private final Context context;
|
||||
|
||||
AngleUnitsChanger(@Nonnull Context context) {
|
||||
this.context = context;
|
||||
this.processor = new DigitButtonDragProcessor(Locator.getInstance().getKeyboard());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDragEvent(@Nonnull DragDirection dragDirection,
|
||||
@Nonnull DragButton dragButton,
|
||||
@Nonnull Point2d startPoint2d,
|
||||
@Nonnull MotionEvent motionEvent) {
|
||||
boolean result = false;
|
||||
|
||||
if (dragButton instanceof AngleUnitsButton) {
|
||||
if (dragDirection != DragDirection.left) {
|
||||
final String directionText = ((AngleUnitsButton) dragButton).getText(dragDirection);
|
||||
if (directionText != null) {
|
||||
try {
|
||||
|
||||
final AngleUnit angleUnits = AngleUnit.valueOf(directionText);
|
||||
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
final AngleUnit oldAngleUnits = AndroidCalculatorEngine.Preferences.angleUnit.getPreference(preferences);
|
||||
if (oldAngleUnits != angleUnits) {
|
||||
Locator.getInstance().getPreferenceService().setAngleUnits(angleUnits);
|
||||
}
|
||||
|
||||
result = true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(this.getClass().getName(), "Unsupported angle units: " + directionText);
|
||||
}
|
||||
}
|
||||
} else if (dragDirection == DragDirection.left) {
|
||||
result = processor.processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static class NumeralBasesChanger implements SimpleOnDragListener.DragProcessor {
|
||||
|
||||
@Nonnull
|
||||
private final Context context;
|
||||
|
||||
NumeralBasesChanger(@Nonnull Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDragEvent(@Nonnull DragDirection dragDirection,
|
||||
@Nonnull DragButton dragButton,
|
||||
@Nonnull Point2d startPoint2d,
|
||||
@Nonnull MotionEvent motionEvent) {
|
||||
boolean result = false;
|
||||
|
||||
if (dragButton instanceof NumeralBasesButton) {
|
||||
final String directionText = ((NumeralBasesButton) dragButton).getText(dragDirection);
|
||||
if (directionText != null) {
|
||||
try {
|
||||
|
||||
final NumeralBase numeralBase = NumeralBase.valueOf(directionText);
|
||||
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
final NumeralBase oldNumeralBase = AndroidCalculatorEngine.Preferences.numeralBase.getPreference(preferences);
|
||||
if (oldNumeralBase != numeralBase) {
|
||||
Locator.getInstance().getPreferenceService().setNumeralBase(numeralBase);
|
||||
}
|
||||
|
||||
result = true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(this.getClass().getName(), "Unsupported numeral base: " + directionText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static class FunctionsDragProcessor implements SimpleOnDragListener.DragProcessor {
|
||||
|
||||
@Nonnull
|
||||
private Context context;
|
||||
|
||||
FunctionsDragProcessor(@Nonnull Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDragEvent(@Nonnull DragDirection dragDirection,
|
||||
@Nonnull DragButton dragButton,
|
||||
@Nonnull Point2d startPoint2d,
|
||||
@Nonnull MotionEvent motionEvent) {
|
||||
boolean result = false;
|
||||
|
||||
if (dragDirection == DragDirection.up) {
|
||||
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_create_function_dialog, null, context);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
import jscl.math.Generic;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
import org.solovyev.android.calculator.plot.CalculatorPlotter;
|
||||
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
|
||||
import org.solovyev.android.menu.LabeledMenuItem;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 21.09.12
|
||||
* Time: 10:55
|
||||
*/
|
||||
public enum CalculatorDisplayMenuItem implements LabeledMenuItem<CalculatorDisplayViewState> {
|
||||
|
||||
copy(R.string.c_copy) {
|
||||
@Override
|
||||
public void onClick(@Nonnull CalculatorDisplayViewState data, @Nonnull Context context) {
|
||||
Locator.getInstance().getKeyboard().copyButtonPressed();
|
||||
}
|
||||
},
|
||||
|
||||
convert_to_bin(R.string.convert_to_bin) {
|
||||
@Override
|
||||
public void onClick(@Nonnull CalculatorDisplayViewState data, @Nonnull Context context) {
|
||||
ConversionMenuItem.convert_to_bin.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@Nonnull Generic generic, @Nonnull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
convert_to_dec(R.string.convert_to_dec) {
|
||||
@Override
|
||||
public void onClick(@Nonnull CalculatorDisplayViewState data, @Nonnull Context context) {
|
||||
ConversionMenuItem.convert_to_dec.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@Nonnull Generic generic, @Nonnull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
convert_to_hex(R.string.convert_to_hex) {
|
||||
@Override
|
||||
public void onClick(@Nonnull CalculatorDisplayViewState data, @Nonnull Context context) {
|
||||
ConversionMenuItem.convert_to_hex.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@Nonnull Generic generic, @Nonnull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
convert(R.string.c_convert) {
|
||||
@Override
|
||||
public void onClick(@Nonnull CalculatorDisplayViewState data, @Nonnull Context context) {
|
||||
final Generic result = data.getResult();
|
||||
if (result != null) {
|
||||
new NumeralBaseConverterDialog(result.toString()).show(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@Nonnull Generic generic, @Nonnull JsclOperation operation) {
|
||||
return operation == JsclOperation.numeric && generic.getConstants().isEmpty();
|
||||
}
|
||||
},
|
||||
|
||||
plot(R.string.c_plot) {
|
||||
@Override
|
||||
public void onClick(@Nonnull CalculatorDisplayViewState data, @Nonnull Context context) {
|
||||
final Generic expression = data.getResult();
|
||||
assert expression != null;
|
||||
|
||||
final CalculatorPlotter plotter = Locator.getInstance().getPlotter();
|
||||
plotter.plot(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@Nonnull Generic generic, @Nonnull JsclOperation operation) {
|
||||
return Locator.getInstance().getPlotter().isPlotPossibleFor(generic);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private final int captionId;
|
||||
|
||||
CalculatorDisplayMenuItem(int captionId) {
|
||||
this.captionId = captionId;
|
||||
}
|
||||
|
||||
public final boolean isItemVisible(@Nonnull CalculatorDisplayViewState displayViewState) {
|
||||
//noinspection ConstantConditions
|
||||
return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
|
||||
}
|
||||
|
||||
protected boolean isItemVisibleFor(@Nonnull Generic generic, @Nonnull JsclOperation operation) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getCaption(@Nonnull Context context) {
|
||||
return context.getString(captionId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.View;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.solovyev.android.menu.ContextMenuBuilder;
|
||||
import org.solovyev.android.menu.ListContextMenu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 21.09.12
|
||||
* Time: 10:58
|
||||
*/
|
||||
public class CalculatorDisplayOnClickListener implements View.OnClickListener {
|
||||
|
||||
@Nonnull
|
||||
private final FragmentActivity activity;
|
||||
|
||||
public CalculatorDisplayOnClickListener(@Nonnull FragmentActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v instanceof CalculatorDisplayView) {
|
||||
final CalculatorDisplay cd = Locator.getInstance().getDisplay();
|
||||
|
||||
final CalculatorDisplayViewState displayViewState = cd.getViewState();
|
||||
|
||||
if (displayViewState.isValid()) {
|
||||
final List<CalculatorDisplayMenuItem> filteredMenuItems = new ArrayList<CalculatorDisplayMenuItem>(CalculatorDisplayMenuItem.values().length);
|
||||
for (CalculatorDisplayMenuItem menuItem : CalculatorDisplayMenuItem.values()) {
|
||||
if (menuItem.isItemVisible(displayViewState)) {
|
||||
filteredMenuItems.add(menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (!filteredMenuItems.isEmpty()) {
|
||||
ContextMenuBuilder.newInstance(activity, "display-menu", ListContextMenu.newInstance(filteredMenuItems)).build(displayViewState).show();
|
||||
}
|
||||
|
||||
} else {
|
||||
final String errorMessage = displayViewState.getErrorMessage();
|
||||
if (errorMessage != null) {
|
||||
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_evaluation_error, errorMessage, activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import jscl.AngleUnit;
|
||||
import jscl.NumeralBase;
|
||||
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.calculator.math.MathType;
|
||||
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
|
||||
import org.solovyev.android.prefs.*;
|
||||
import org.solovyev.android.view.VibratorContainer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.solovyev.android.Android.isPhoneModel;
|
||||
import static org.solovyev.android.DeviceModel.samsung_galaxy_s;
|
||||
import static org.solovyev.android.DeviceModel.samsung_galaxy_s_2;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 4/20/12
|
||||
* Time: 12:42 PM
|
||||
*/
|
||||
public final class CalculatorPreferences {
|
||||
|
||||
private CalculatorPreferences() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static final Preference<Integer> appVersion = IntegerPreference.of("application.version", -1);
|
||||
public static final Preference<Integer> appOpenedCounter = IntegerPreference.of("app_opened_counter", 0);
|
||||
|
||||
public static class OnscreenCalculator {
|
||||
public static final Preference<Boolean> startOnBoot = BooleanPreference.of("onscreen_start_on_boot", false);
|
||||
public static final Preference<Boolean> showAppIcon = BooleanPreference.of("onscreen_show_app_icon", true);
|
||||
}
|
||||
|
||||
public static class Calculations {
|
||||
|
||||
public static final Preference<Boolean> calculateOnFly = BooleanPreference.of("calculations_calculate_on_fly", true);
|
||||
public static final Preference<Boolean> showCalculationMessagesDialog = BooleanPreference.of("show_calculation_messages_dialog", true);
|
||||
|
||||
public static final Preference<NumeralBase> preferredNumeralBase = StringPreference.ofEnum("preferred_numeral_base", AndroidCalculatorEngine.Preferences.numeralBase.getDefaultValue(), NumeralBase.class);
|
||||
public static final Preference<AngleUnit> preferredAngleUnits = StringPreference.ofEnum("preferred_angle_units", AndroidCalculatorEngine.Preferences.angleUnit.getDefaultValue(), AngleUnit.class);
|
||||
public static final Preference<Long> lastPreferredPreferencesCheck = LongPreference.of("preferred_preferences_check_time", 0L);
|
||||
|
||||
}
|
||||
|
||||
public static class Gui {
|
||||
|
||||
public static final Preference<Theme> theme = StringPreference.ofEnum("org.solovyev.android.calculator.CalculatorActivity_calc_theme", Theme.metro_blue_theme, Theme.class);
|
||||
public static final Preference<Layout> layout = StringPreference.ofEnum("org.solovyev.android.calculator.CalculatorActivity_calc_layout", Layout.simple, Layout.class);
|
||||
public static final Preference<Boolean> feedbackWindowShown = BooleanPreference.of("feedback_window_shown", false);
|
||||
public static final Preference<Boolean> notesppAnnounceShown = BooleanPreference.of("notespp_announce_shown", false);
|
||||
public static final Preference<Boolean> showReleaseNotes = BooleanPreference.of("org.solovyev.android.calculator.CalculatorActivity_show_release_notes", true);
|
||||
public static final Preference<Boolean> usePrevAsBack = BooleanPreference.of("org.solovyev.android.calculator.CalculatorActivity_use_back_button_as_prev", false);
|
||||
public static final Preference<Boolean> showEqualsButton = BooleanPreference.of("showEqualsButton", true);
|
||||
public static final Preference<Boolean> autoOrientation = BooleanPreference.of("autoOrientation", true);
|
||||
public static final Preference<Boolean> hideNumeralBaseDigits = BooleanPreference.of("hideNumeralBaseDigits", true);
|
||||
public static final Preference<Boolean> preventScreenFromFading = BooleanPreference.of("preventScreenFromFading", true);
|
||||
public static final Preference<Boolean> colorDisplay = BooleanPreference.of("org.solovyev.android.calculator.CalculatorModel_color_display", true);
|
||||
|
||||
@Nonnull
|
||||
public static Theme getTheme(@Nonnull SharedPreferences preferences) {
|
||||
return theme.getPreferenceNoError(preferences);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Layout getLayout(@Nonnull SharedPreferences preferences) {
|
||||
return layout.getPreferenceNoError(preferences);
|
||||
}
|
||||
|
||||
public static enum Theme {
|
||||
|
||||
default_theme(ThemeType.other, R.style.cpp_gray_theme),
|
||||
violet_theme(ThemeType.other, R.style.cpp_violet_theme),
|
||||
light_blue_theme(ThemeType.other, R.style.cpp_light_blue_theme),
|
||||
metro_blue_theme(ThemeType.metro, R.style.cpp_metro_blue_theme),
|
||||
metro_purple_theme(ThemeType.metro, R.style.cpp_metro_purple_theme),
|
||||
metro_green_theme(ThemeType.metro, R.style.cpp_metro_green_theme);
|
||||
|
||||
@Nonnull
|
||||
private final ThemeType themeType;
|
||||
|
||||
@Nonnull
|
||||
private final Integer themeId;
|
||||
|
||||
Theme(@Nonnull ThemeType themeType, @Nonnull Integer themeId) {
|
||||
this.themeType = themeType;
|
||||
this.themeId = themeId;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ThemeType getThemeType() {
|
||||
return themeType;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Integer getThemeId() {
|
||||
return themeId;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum ThemeType {
|
||||
metro,
|
||||
other
|
||||
}
|
||||
|
||||
public static enum Layout {
|
||||
main_calculator(R.layout.main_calculator, R.string.p_layout_calculator, true),
|
||||
main_calculator_mobile(R.layout.main_calculator_mobile, R.string.p_layout_calculator_mobile, false),
|
||||
|
||||
// not used anymore
|
||||
@Deprecated
|
||||
main_cellphone(R.layout.main_calculator, 0, true),
|
||||
|
||||
simple(R.layout.main_calculator, R.string.p_layout_simple, true),
|
||||
simple_mobile(R.layout.main_calculator_mobile, R.string.p_layout_simple_mobile, false);
|
||||
|
||||
private final int layoutId;
|
||||
private final int nameResId;
|
||||
private final boolean optimized;
|
||||
|
||||
Layout(int layoutId, int nameResId, boolean optimized) {
|
||||
this.layoutId = layoutId;
|
||||
this.nameResId = nameResId;
|
||||
this.optimized = optimized;
|
||||
}
|
||||
|
||||
public int getLayoutId() {
|
||||
return layoutId;
|
||||
}
|
||||
|
||||
public int getNameResId() {
|
||||
return nameResId;
|
||||
}
|
||||
|
||||
public boolean isOptimized() {
|
||||
return optimized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Graph {
|
||||
public static final Preference<Boolean> plotImag = BooleanPreference.of("graph_plot_imag", false);
|
||||
}
|
||||
|
||||
public static class History {
|
||||
public static final Preference<Boolean> showIntermediateCalculations = BooleanPreference.of("history_show_intermediate_calculations", false);
|
||||
public static final Preference<Boolean> showDatetime = BooleanPreference.of("history_show_datetime", true);
|
||||
}
|
||||
|
||||
|
||||
static void setDefaultValues(@Nonnull SharedPreferences preferences) {
|
||||
|
||||
if (!AndroidCalculatorEngine.Preferences.groupingSeparator.isSet(preferences)) {
|
||||
final Locale locale = Locale.getDefault();
|
||||
if (locale != null) {
|
||||
final DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(locale);
|
||||
int index = MathType.grouping_separator.getTokens().indexOf(String.valueOf(decimalFormatSymbols.getGroupingSeparator()));
|
||||
final String groupingSeparator;
|
||||
if (index >= 0) {
|
||||
groupingSeparator = MathType.grouping_separator.getTokens().get(index);
|
||||
} else {
|
||||
groupingSeparator = " ";
|
||||
}
|
||||
|
||||
AndroidCalculatorEngine.Preferences.groupingSeparator.putPreference(preferences, groupingSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!AndroidCalculatorEngine.Preferences.angleUnit.isSet(preferences)) {
|
||||
AndroidCalculatorEngine.Preferences.angleUnit.putDefault(preferences);
|
||||
}
|
||||
|
||||
if (!AndroidCalculatorEngine.Preferences.numeralBase.isSet(preferences)) {
|
||||
AndroidCalculatorEngine.Preferences.numeralBase.putDefault(preferences);
|
||||
}
|
||||
|
||||
if (!AndroidCalculatorEngine.Preferences.multiplicationSign.isSet(preferences)) {
|
||||
if (isPhoneModel(samsung_galaxy_s) || isPhoneModel(samsung_galaxy_s_2)) {
|
||||
// workaround ofr samsung galaxy s phones
|
||||
AndroidCalculatorEngine.Preferences.multiplicationSign.putPreference(preferences, "*");
|
||||
}
|
||||
}
|
||||
|
||||
applyDefaultPreference(preferences, Gui.theme);
|
||||
applyDefaultPreference(preferences, Gui.layout);
|
||||
if (Gui.layout.getPreference(preferences) == Gui.Layout.main_cellphone) {
|
||||
Gui.layout.putDefault(preferences);
|
||||
}
|
||||
applyDefaultPreference(preferences, Gui.feedbackWindowShown);
|
||||
applyDefaultPreference(preferences, Gui.notesppAnnounceShown);
|
||||
applyDefaultPreference(preferences, Gui.showReleaseNotes);
|
||||
applyDefaultPreference(preferences, Gui.usePrevAsBack);
|
||||
applyDefaultPreference(preferences, Gui.showEqualsButton);
|
||||
applyDefaultPreference(preferences, Gui.autoOrientation);
|
||||
applyDefaultPreference(preferences, Gui.hideNumeralBaseDigits);
|
||||
applyDefaultPreference(preferences, Gui.preventScreenFromFading);
|
||||
|
||||
applyDefaultPreference(preferences, Graph.plotImag);
|
||||
applyDefaultPreference(preferences, History.showIntermediateCalculations);
|
||||
applyDefaultPreference(preferences, History.showDatetime);
|
||||
applyDefaultPreference(preferences, Calculations.calculateOnFly);
|
||||
applyDefaultPreference(preferences, Calculations.preferredAngleUnits);
|
||||
applyDefaultPreference(preferences, Calculations.preferredNumeralBase);
|
||||
|
||||
applyDefaultPreference(preferences, OnscreenCalculator.showAppIcon);
|
||||
applyDefaultPreference(preferences, OnscreenCalculator.startOnBoot);
|
||||
|
||||
|
||||
// renew value after each application start
|
||||
Calculations.showCalculationMessagesDialog.putDefault(preferences);
|
||||
Calculations.lastPreferredPreferencesCheck.putDefault(preferences);
|
||||
|
||||
if (!VibratorContainer.Preferences.hapticFeedbackEnabled.isSet(preferences)) {
|
||||
VibratorContainer.Preferences.hapticFeedbackEnabled.putPreference(preferences, true);
|
||||
}
|
||||
|
||||
if (!VibratorContainer.Preferences.hapticFeedbackDuration.isSet(preferences)) {
|
||||
VibratorContainer.Preferences.hapticFeedbackDuration.putPreference(preferences, 60L);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void applyDefaultPreference(@Nonnull SharedPreferences preferences, @Nonnull Preference<?> preference) {
|
||||
preference.tryPutDefault(preferences);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class CalculatorReceiver extends BroadcastReceiver {
|
||||
|
||||
public static final String ACTION_BUTTON_ID_EXTRA = "buttonId";
|
||||
public static final String ACTION_BUTTON_PRESSED = "org.solovyev.android.calculator.BUTTON_PRESSED";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
|
||||
if (ACTION_BUTTON_PRESSED.equals(action)) {
|
||||
final int buttonId = intent.getIntExtra(ACTION_BUTTON_ID_EXTRA, 0);
|
||||
|
||||
final CalculatorButton button = CalculatorButton.getById(buttonId);
|
||||
if (button != null) {
|
||||
button.onClick(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Intent newButtonClickedIntent(@Nonnull Context context, @Nonnull CalculatorButton button) {
|
||||
final Intent intent = new Intent(context, CalculatorReceiver.class);
|
||||
intent.setAction(ACTION_BUTTON_PRESSED);
|
||||
intent.putExtra(ACTION_BUTTON_ID_EXTRA, button.getButtonId());
|
||||
return intent;
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
import jscl.NumeralBase;
|
||||
import jscl.math.Generic;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
import org.solovyev.android.menu.AMenuItem;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/21/12
|
||||
* Time: 12:11 AM
|
||||
*/
|
||||
enum ConversionMenuItem implements AMenuItem<CalculatorDisplayViewState> {
|
||||
|
||||
convert_to_bin(NumeralBase.bin),
|
||||
convert_to_dec(NumeralBase.dec),
|
||||
convert_to_hex(NumeralBase.hex);
|
||||
|
||||
@Nonnull
|
||||
private final NumeralBase toNumeralBase;
|
||||
|
||||
ConversionMenuItem(@Nonnull NumeralBase toNumeralBase) {
|
||||
this.toNumeralBase = toNumeralBase;
|
||||
}
|
||||
|
||||
protected boolean isItemVisibleFor(@Nonnull Generic generic, @Nonnull JsclOperation operation) {
|
||||
boolean result = false;
|
||||
|
||||
if (operation == JsclOperation.numeric) {
|
||||
if (generic.getConstants().isEmpty()) {
|
||||
// conversion possible => return true
|
||||
final NumeralBase fromNumeralBase = Locator.getInstance().getEngine().getNumeralBase();
|
||||
if (fromNumeralBase != toNumeralBase) {
|
||||
result = Locator.getInstance().getCalculator().isConversionPossible(generic, fromNumeralBase, this.toNumeralBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(@Nonnull CalculatorDisplayViewState data, @Nonnull Context context) {
|
||||
final Generic result = data.getResult();
|
||||
|
||||
if (result != null) {
|
||||
Locator.getInstance().getCalculator().convert(result, this.toNumeralBase);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.solovyev.android.view.drag.DirectionDragButton;
|
||||
import org.solovyev.android.view.drag.DragButton;
|
||||
import org.solovyev.android.view.drag.DragDirection;
|
||||
import org.solovyev.android.view.drag.SimpleOnDragListener;
|
||||
import org.solovyev.common.math.Point2d;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/16/11
|
||||
* Time: 11:48 PM
|
||||
*/
|
||||
public class DigitButtonDragProcessor implements SimpleOnDragListener.DragProcessor {
|
||||
|
||||
@Nonnull
|
||||
private CalculatorKeyboard calculatorKeyboard;
|
||||
|
||||
public DigitButtonDragProcessor(@Nonnull CalculatorKeyboard calculatorKeyboard) {
|
||||
this.calculatorKeyboard = calculatorKeyboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDragEvent(@Nonnull DragDirection dragDirection, @Nonnull DragButton dragButton, @Nonnull Point2d startPoint2d, @Nonnull MotionEvent motionEvent) {
|
||||
assert dragButton instanceof DirectionDragButton;
|
||||
calculatorKeyboard.buttonPressed(((DirectionDragButton) dragButton).getText(dragDirection));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import org.solovyev.common.msg.Message;
|
||||
import org.solovyev.common.msg.MessageType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/17/12
|
||||
* Time: 6:54 PM
|
||||
*/
|
||||
public class FixableMessage implements Parcelable {
|
||||
|
||||
public static final Creator<FixableMessage> CREATOR = new Creator<FixableMessage>() {
|
||||
@Override
|
||||
public FixableMessage createFromParcel(@Nonnull Parcel in) {
|
||||
return FixableMessage.fromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FixableMessage[] newArray(int size) {
|
||||
return new FixableMessage[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Nonnull
|
||||
private static FixableMessage fromParcel(@Nonnull Parcel in) {
|
||||
final String message = in.readString();
|
||||
final MessageType messageType = (MessageType) in.readSerializable();
|
||||
final FixableError fixableError = (FixableError) in.readSerializable();
|
||||
|
||||
return new FixableMessage(message, messageType, fixableError);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private final String message;
|
||||
|
||||
@Nonnull
|
||||
private final MessageType messageType;
|
||||
|
||||
@Nullable
|
||||
private final FixableError fixableError;
|
||||
|
||||
public FixableMessage(@Nonnull Message message) {
|
||||
this.message = message.getLocalizedMessage();
|
||||
final int messageLevel = message.getMessageLevel().getMessageLevel();
|
||||
this.messageType = CalculatorMessages.toMessageType(messageLevel);
|
||||
this.fixableError = CalculatorFixableError.getErrorByMessageCode(message.getMessageCode());
|
||||
}
|
||||
|
||||
public FixableMessage(@Nonnull String message,
|
||||
@Nonnull MessageType messageType,
|
||||
@Nullable FixableError fixableError) {
|
||||
this.message = message;
|
||||
this.messageType = messageType;
|
||||
this.fixableError = fixableError;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@Nonnull Parcel out, int flags) {
|
||||
out.writeString(message);
|
||||
out.writeSerializable(messageType);
|
||||
out.writeSerializable(fixableError);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MessageType getMessageType() {
|
||||
return messageType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FixableError getFixableError() {
|
||||
return fixableError;
|
||||
}
|
||||
}
|
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockActivity;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.common.msg.Message;
|
||||
import org.solovyev.common.text.Strings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/17/12
|
||||
* Time: 3:37 PM
|
||||
*/
|
||||
public class FixableMessagesDialog extends SherlockActivity {
|
||||
|
||||
private static final String INPUT = "input";
|
||||
|
||||
@Nonnull
|
||||
private Input input = new Input(Collections.<FixableMessage>emptyList(), false);
|
||||
|
||||
public FixableMessagesDialog() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.cpp_fixable_messages_dialog);
|
||||
|
||||
final Intent intent = getIntent();
|
||||
if (intent != null) {
|
||||
parseIntent(intent);
|
||||
}
|
||||
|
||||
final CheckBox doNotShowCalculationMessagesCheckbox = (CheckBox) findViewById(R.id.cpp_do_not_show_fixable_messages_checkbox);
|
||||
if (input.isShowCheckbox()) {
|
||||
doNotShowCalculationMessagesCheckbox.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
doNotShowCalculationMessagesCheckbox.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
final Button closeButton = (Button) findViewById(R.id.close_button);
|
||||
closeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (doNotShowCalculationMessagesCheckbox.isChecked()) {
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(FixableMessagesDialog.this);
|
||||
CalculatorPreferences.Calculations.showCalculationMessagesDialog.putPreference(prefs, false);
|
||||
}
|
||||
|
||||
FixableMessagesDialog.this.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void parseIntent(@Nonnull Intent intent) {
|
||||
final Input input = intent.getParcelableExtra(INPUT);
|
||||
if (input != null) {
|
||||
this.input = input;
|
||||
onInputChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void onInputChanged() {
|
||||
final ViewGroup viewGroup = (ViewGroup) findViewById(R.id.cpp_fixable_messages_container);
|
||||
viewGroup.removeAllViews();
|
||||
|
||||
final LayoutInflater layoutInflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
final List<FixableMessage> messages = input.getMessages();
|
||||
for (final FixableMessage message : messages) {
|
||||
final View view = layoutInflater.inflate(R.layout.cpp_fixable_messages_dialog_message, null);
|
||||
|
||||
final TextView calculationMessagesTextView = (TextView) view.findViewById(R.id.cpp_fixable_messages_text_view);
|
||||
calculationMessagesTextView.setText(message.getMessage());
|
||||
|
||||
final Button fixButton = (Button) view.findViewById(R.id.cpp_fix_button);
|
||||
final FixableError fixableError = message.getFixableError();
|
||||
if (fixableError == null) {
|
||||
fixButton.setVisibility(View.GONE);
|
||||
fixButton.setOnClickListener(null);
|
||||
} else {
|
||||
fixButton.setVisibility(View.VISIBLE);
|
||||
fixButton.setOnClickListener(new FixErrorOnClickListener(messages, message));
|
||||
|
||||
final CharSequence fixCaption = fixableError.getFixCaption();
|
||||
if (!Strings.isEmpty(fixCaption)) {
|
||||
fixButton.setText(fixCaption);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
viewGroup.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(@Nonnull Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
parseIntent(intent);
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* STATIC
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public static void showDialogForMessages(@Nonnull List<Message> messages,
|
||||
@Nonnull Context context,
|
||||
boolean showCheckbox) {
|
||||
if (!messages.isEmpty()) {
|
||||
final Intent intent = new Intent(context, FixableMessagesDialog.class);
|
||||
|
||||
intent.putExtra(INPUT, Input.fromMessages(messages, showCheckbox));
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void showDialog(@Nonnull List<FixableMessage> messages,
|
||||
@Nonnull Context context,
|
||||
boolean showCheckbox) {
|
||||
if (!messages.isEmpty()) {
|
||||
final Intent intent = new Intent(context, FixableMessagesDialog.class);
|
||||
|
||||
intent.putExtra(INPUT, new Input(messages, showCheckbox));
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Input implements Parcelable {
|
||||
|
||||
public static final Creator<Input> CREATOR = new Creator<Input>() {
|
||||
@Override
|
||||
public Input createFromParcel(@Nonnull Parcel in) {
|
||||
return Input.fromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Input[] newArray(int size) {
|
||||
return new Input[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Nonnull
|
||||
private static Input fromParcel(@Nonnull Parcel in) {
|
||||
final List<FixableMessage> messages = new ArrayList<FixableMessage>();
|
||||
boolean showCheckbox = in.readInt() == 1;
|
||||
in.readTypedList(messages, FixableMessage.CREATOR);
|
||||
return new Input(messages, showCheckbox);
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
private List<FixableMessage> messages = new ArrayList<FixableMessage>();
|
||||
|
||||
private boolean showCheckbox;
|
||||
|
||||
private Input(@Nonnull List<FixableMessage> messages, boolean showCheckbox) {
|
||||
this.messages = messages;
|
||||
this.showCheckbox = showCheckbox;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<FixableMessage> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@Nonnull Parcel out, int flags) {
|
||||
out.writeInt(showCheckbox ? 1 : 0);
|
||||
out.writeTypedList(messages);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Input fromMessages(@Nonnull List<Message> messages, boolean showCheckbox) {
|
||||
final List<FixableMessage> stringMessages = new ArrayList<FixableMessage>(messages.size());
|
||||
for (Message message : messages) {
|
||||
stringMessages.add(new FixableMessage(message));
|
||||
}
|
||||
|
||||
return new Input(stringMessages, showCheckbox);
|
||||
}
|
||||
|
||||
public boolean isShowCheckbox() {
|
||||
return showCheckbox;
|
||||
}
|
||||
}
|
||||
|
||||
private class FixErrorOnClickListener implements View.OnClickListener {
|
||||
|
||||
@Nonnull
|
||||
private final List<FixableMessage> messages;
|
||||
|
||||
@Nonnull
|
||||
private final FixableMessage currentMessage;
|
||||
|
||||
public FixErrorOnClickListener(@Nonnull List<FixableMessage> messages,
|
||||
@Nonnull FixableMessage message) {
|
||||
this.messages = messages;
|
||||
this.currentMessage = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final List<FixableMessage> filteredMessages = new ArrayList<FixableMessage>(messages.size() - 1);
|
||||
for (FixableMessage message : messages) {
|
||||
if (message.getFixableError() == null) {
|
||||
filteredMessages.add(message);
|
||||
} else if (message.getFixableError() != currentMessage.getFixableError()) {
|
||||
filteredMessages.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
currentMessage.getFixableError().fix();
|
||||
|
||||
if (!filteredMessages.isEmpty()) {
|
||||
FixableMessagesDialog.this.input = new Input(filteredMessages, FixableMessagesDialog.this.input.showCheckbox);
|
||||
onInputChanged();
|
||||
} else {
|
||||
FixableMessagesDialog.this.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import org.solovyev.common.msg.MessageLevel;
|
||||
import org.solovyev.common.msg.MessageType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 1/20/13
|
||||
* Time: 1:04 PM
|
||||
*/
|
||||
public final class ParcelableDialogData implements DialogData, Parcelable {
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* STATIC
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public final static Creator<ParcelableDialogData> CREATOR = new Creator<ParcelableDialogData>() {
|
||||
@Override
|
||||
public ParcelableDialogData createFromParcel(@Nonnull Parcel in) {
|
||||
return fromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelableDialogData[] newArray(int size) {
|
||||
return new ParcelableDialogData[size];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* FIELDS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
@Nonnull
|
||||
private DialogData nestedData;
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* CONSTRUCTORS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public ParcelableDialogData(@Nonnull DialogData nestedData) {
|
||||
this.nestedData = nestedData;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static ParcelableDialogData wrap(@Nonnull DialogData nestedData) {
|
||||
if (nestedData instanceof ParcelableDialogData) {
|
||||
return ((ParcelableDialogData) nestedData);
|
||||
} else {
|
||||
return new ParcelableDialogData(nestedData);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static ParcelableDialogData fromParcel(@Nonnull Parcel in) {
|
||||
final String message = in.readString();
|
||||
final MessageType messageType = CalculatorMessages.toMessageType(in.readInt());
|
||||
final String title = in.readString();
|
||||
return wrap(StringDialogData.newInstance(message, messageType, title));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return nestedData.getMessage();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MessageLevel getMessageLevel() {
|
||||
return nestedData.getMessageLevel();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return nestedData.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@Nonnull Parcel out, int flags) {
|
||||
out.writeString(this.getMessage());
|
||||
out.writeInt(this.getMessageLevel().getMessageLevel());
|
||||
out.writeString(this.getTitle());
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
/**
|
||||
* Marker interface for service locator
|
||||
*/
|
||||
public interface ServiceLocator {
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.external;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/20/12
|
||||
* Time: 10:33 PM
|
||||
*/
|
||||
public interface ExternalCalculatorIntentHandler {
|
||||
|
||||
void onIntent(@Nonnull Context context, @Nonnull Intent intent);
|
||||
}
|
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import jscl.AngleUnit;
|
||||
import jscl.JsclMathEngine;
|
||||
import jscl.MathEngine;
|
||||
import jscl.NumeralBase;
|
||||
import jscl.math.function.Function;
|
||||
import jscl.math.function.IConstant;
|
||||
import jscl.math.operator.Operator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.solovyev.android.calculator.*;
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.prefs.BooleanPreference;
|
||||
import org.solovyev.android.prefs.Preference;
|
||||
import org.solovyev.android.prefs.StringPreference;
|
||||
import org.solovyev.common.text.EnumMapper;
|
||||
import org.solovyev.common.text.NumberMapper;
|
||||
import org.solovyev.common.text.Strings;
|
||||
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/12/11
|
||||
* Time: 11:38 PM
|
||||
*/
|
||||
|
||||
public class AndroidCalculatorEngine implements CalculatorEngine, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator";
|
||||
|
||||
private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign";
|
||||
private static final String MULTIPLICATION_SIGN_DEFAULT = "×";
|
||||
|
||||
private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time";
|
||||
private static final String MAX_CALCULATION_TIME_DEFAULT = "5";
|
||||
|
||||
private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation";
|
||||
private static final boolean SCIENCE_NOTATION_DEFAULT = false;
|
||||
|
||||
private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result";
|
||||
private static final boolean ROUND_RESULT_DEFAULT = true;
|
||||
|
||||
private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision";
|
||||
private static final String RESULT_PRECISION_DEFAULT = "5";
|
||||
|
||||
private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases";
|
||||
private static final String NUMERAL_BASES_DEFAULT = "dec";
|
||||
|
||||
private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units";
|
||||
private static final String ANGLE_UNITS_DEFAULT = "deg";
|
||||
|
||||
public static class Preferences {
|
||||
public static final Preference<String> groupingSeparator = StringPreference.of(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT);
|
||||
public static final Preference<String> multiplicationSign = StringPreference.of(MULTIPLICATION_SIGN_P_KEY, MULTIPLICATION_SIGN_DEFAULT);
|
||||
public static final Preference<Integer> precision = StringPreference.ofTypedValue(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT, NumberMapper.of(Integer.class));
|
||||
public static final Preference<Boolean> roundResult = BooleanPreference.of(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT);
|
||||
public static final Preference<NumeralBase> numeralBase = StringPreference.ofTypedValue(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT, EnumMapper.of(NumeralBase.class));
|
||||
public static final Preference<AngleUnit> angleUnit = StringPreference.ofTypedValue(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT, EnumMapper.of(AngleUnit.class));
|
||||
public static final Preference<Boolean> scienceNotation = BooleanPreference.of(SCIENCE_NOTATION_P_KEY, SCIENCE_NOTATION_DEFAULT);
|
||||
public static final Preference<Integer> maxCalculationTime = StringPreference.ofTypedValue(MAX_CALCULATION_TIME_P_KEY, MAX_CALCULATION_TIME_DEFAULT, NumberMapper.of(Integer.class));
|
||||
|
||||
private static final List<String> preferenceKeys = new ArrayList<String>();
|
||||
|
||||
static {
|
||||
preferenceKeys.add(groupingSeparator.getKey());
|
||||
preferenceKeys.add(multiplicationSign.getKey());
|
||||
preferenceKeys.add(precision.getKey());
|
||||
preferenceKeys.add(roundResult.getKey());
|
||||
preferenceKeys.add(numeralBase.getKey());
|
||||
preferenceKeys.add(angleUnit.getKey());
|
||||
preferenceKeys.add(scienceNotation.getKey());
|
||||
preferenceKeys.add(maxCalculationTime.getKey());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static List<String> getPreferenceKeys() {
|
||||
return Collections.unmodifiableList(preferenceKeys);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private final Context context;
|
||||
|
||||
@Nonnull
|
||||
private final CalculatorEngine calculatorEngine;
|
||||
|
||||
@Nonnull
|
||||
private final Object lock;
|
||||
|
||||
public AndroidCalculatorEngine(@Nonnull Application application) {
|
||||
this.context = application;
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(application).registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
this.lock = new Object();
|
||||
|
||||
final JsclMathEngine engine = JsclMathEngine.getInstance();
|
||||
this.calculatorEngine = new CalculatorEngineImpl(engine,
|
||||
new CalculatorVarsRegistry(engine.getConstantsRegistry(), new AndroidMathEntityDao<Var>("org.solovyev.android.calculator.CalculatorModel_vars", application, Vars.class)),
|
||||
new CalculatorFunctionsMathRegistry(engine.getFunctionsRegistry(), new AndroidMathEntityDao<AFunction>("org.solovyev.android.calculator.CalculatorModel_functions", application, Functions.class)),
|
||||
new CalculatorOperatorsMathRegistry(engine.getOperatorsRegistry(), new AndroidMathEntityDao<MathPersistenceEntity>(null, application, null)),
|
||||
new CalculatorPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry(), new AndroidMathEntityDao<MathPersistenceEntity>(null, application, null)),
|
||||
this.lock);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public CalculatorMathRegistry<IConstant> getVarsRegistry() {
|
||||
return calculatorEngine.getVarsRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public CalculatorMathRegistry<Function> getFunctionsRegistry() {
|
||||
return calculatorEngine.getFunctionsRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public CalculatorMathRegistry<Operator> getOperatorsRegistry() {
|
||||
return calculatorEngine.getOperatorsRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public CalculatorMathRegistry<Operator> getPostfixFunctionsRegistry() {
|
||||
return calculatorEngine.getPostfixFunctionsRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public CalculatorMathEngine getMathEngine() {
|
||||
return calculatorEngine.getMathEngine();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MathEngine getMathEngine0() {
|
||||
return calculatorEngine.getMathEngine0();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public NumeralBase getNumeralBase() {
|
||||
return calculatorEngine.getNumeralBase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
synchronized (lock) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
synchronized (lock) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
softReset(preferences);
|
||||
|
||||
calculatorEngine.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void softReset() {
|
||||
synchronized (lock) {
|
||||
softReset(PreferenceManager.getDefaultSharedPreferences(context));
|
||||
|
||||
calculatorEngine.softReset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseGroupingSeparator(boolean useGroupingSeparator) {
|
||||
calculatorEngine.setUseGroupingSeparator(useGroupingSeparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupingSeparator(char groupingSeparator) {
|
||||
calculatorEngine.setGroupingSeparator(groupingSeparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrecision(@Nonnull Integer precision) {
|
||||
calculatorEngine.setPrecision(precision);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRoundResult(@Nonnull Boolean round) {
|
||||
calculatorEngine.setRoundResult(round);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public AngleUnit getAngleUnits() {
|
||||
return calculatorEngine.getAngleUnits();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAngleUnits(@Nonnull AngleUnit angleUnits) {
|
||||
calculatorEngine.setAngleUnits(angleUnits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumeralBase(@Nonnull NumeralBase numeralBase) {
|
||||
calculatorEngine.setNumeralBase(numeralBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMultiplicationSign(@Nonnull String multiplicationSign) {
|
||||
calculatorEngine.setMultiplicationSign(multiplicationSign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScienceNotation(@Nonnull Boolean scienceNotation) {
|
||||
calculatorEngine.setScienceNotation(scienceNotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimeout(@Nonnull Integer timeout) {
|
||||
calculatorEngine.setTimeout(timeout);
|
||||
}
|
||||
|
||||
private void softReset(@Nonnull SharedPreferences preferences) {
|
||||
this.setPrecision(Preferences.precision.getPreference(preferences));
|
||||
this.setRoundResult(Preferences.roundResult.getPreference(preferences));
|
||||
this.setAngleUnits(getAngleUnitsFromPrefs(preferences));
|
||||
this.setNumeralBase(getNumeralBaseFromPrefs(preferences));
|
||||
this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences));
|
||||
this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences));
|
||||
this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences));
|
||||
|
||||
final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences);
|
||||
if (Strings.isEmpty(groupingSeparator)) {
|
||||
this.setUseGroupingSeparator(false);
|
||||
} else {
|
||||
this.setUseGroupingSeparator(true);
|
||||
setGroupingSeparator(groupingSeparator.charAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static NumeralBase getNumeralBaseFromPrefs(@Nonnull SharedPreferences preferences) {
|
||||
return Preferences.numeralBase.getPreference(preferences);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static AngleUnit getAngleUnitsFromPrefs(@Nonnull SharedPreferences preferences) {
|
||||
return Preferences.angleUnit.getPreference(preferences);
|
||||
}
|
||||
|
||||
//for tests only
|
||||
public void setDecimalGroupSymbols(@Nonnull DecimalFormatSymbols decimalGroupSymbols) {
|
||||
this.calculatorEngine.setDecimalGroupSymbols(decimalGroupSymbols);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public String getMultiplicationSign() {
|
||||
return calculatorEngine.getMultiplicationSign();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (Preferences.getPreferenceKeys().contains(key)) {
|
||||
this.softReset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.preference.PreferenceManager;
|
||||
import org.simpleframework.xml.Serializer;
|
||||
import org.simpleframework.xml.core.Persister;
|
||||
import org.solovyev.android.calculator.App;
|
||||
import org.solovyev.android.calculator.MathEntityDao;
|
||||
import org.solovyev.android.calculator.MathEntityPersistenceContainer;
|
||||
import org.solovyev.android.calculator.MathPersistenceEntity;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 10/7/12
|
||||
* Time: 6:46 PM
|
||||
*/
|
||||
public class AndroidMathEntityDao<T extends MathPersistenceEntity> implements MathEntityDao<T> {
|
||||
|
||||
@Nonnull
|
||||
private static final String TAG = AndroidMathEntityDao.class.getSimpleName();
|
||||
|
||||
@Nullable
|
||||
private final String preferenceString;
|
||||
|
||||
@Nonnull
|
||||
private final Context context;
|
||||
|
||||
@Nullable
|
||||
private final Class<? extends MathEntityPersistenceContainer<T>> persistenceContainerClass;
|
||||
|
||||
public AndroidMathEntityDao(@Nullable String preferenceString,
|
||||
@Nonnull Application application,
|
||||
@Nullable Class<? extends MathEntityPersistenceContainer<T>> persistenceContainerClass) {
|
||||
this.preferenceString = preferenceString;
|
||||
this.context = application;
|
||||
this.persistenceContainerClass = persistenceContainerClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@Nonnull MathEntityPersistenceContainer<T> container) {
|
||||
if (preferenceString != null) {
|
||||
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final SharedPreferences.Editor editor = settings.edit();
|
||||
|
||||
final StringWriter sw = new StringWriter();
|
||||
final Serializer serializer = new Persister();
|
||||
try {
|
||||
serializer.write(container, sw);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
editor.putString(preferenceString, sw.toString());
|
||||
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MathEntityPersistenceContainer<T> load() {
|
||||
if (persistenceContainerClass != null && preferenceString != null) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
if (preferences != null) {
|
||||
final String value = preferences.getString(preferenceString, null);
|
||||
if (value != null) {
|
||||
final Serializer serializer = new Persister();
|
||||
try {
|
||||
return serializer.read(persistenceContainerClass, value);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDescription(@Nonnull String descriptionId) {
|
||||
final Resources resources = context.getResources();
|
||||
|
||||
final int stringId = resources.getIdentifier(descriptionId, "string", App.getApplication().getClass().getPackage().getName());
|
||||
try {
|
||||
return resources.getString(stringId);
|
||||
} catch (Resources.NotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.onscreen;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.solovyev.android.calculator.CalculatorPreferences;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/20/12
|
||||
* Time: 11:05 PM
|
||||
*/
|
||||
public final class CalculatorOnscreenBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
public CalculatorOnscreenBroadcastReceiver() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(@Nonnull Context context,
|
||||
@Nonnull Intent intent) {
|
||||
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (CalculatorPreferences.OnscreenCalculator.startOnBoot.getPreferenceNoError(preferences)) {
|
||||
CalculatorOnscreenService.showNotification(context);
|
||||
}
|
||||
} else {
|
||||
final Intent newIntent = new Intent(intent);
|
||||
newIntent.setClass(context, CalculatorOnscreenService.class);
|
||||
context.startService(newIntent);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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.onscreen;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.WindowManager;
|
||||
import org.solovyev.android.Views;
|
||||
import org.solovyev.android.calculator.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/20/12
|
||||
* Time: 9:42 PM
|
||||
*/
|
||||
public class CalculatorOnscreenService extends Service implements OnscreenViewListener, CalculatorEventListener {
|
||||
|
||||
private static final String SHOW_WINDOW_ACTION = "org.solovyev.android.calculator.onscreen.SHOW_WINDOW";
|
||||
private static final String SHOW_NOTIFICATION_ACTION = "org.solovyev.android.calculator.onscreen.SHOW_NOTIFICATION";
|
||||
|
||||
private static final int NOTIFICATION_ID = 9031988; // my birthday =)
|
||||
|
||||
public static final Class<CalculatorOnscreenBroadcastReceiver> INTENT_LISTENER_CLASS = CalculatorOnscreenBroadcastReceiver.class;
|
||||
|
||||
@Nonnull
|
||||
private CalculatorOnscreenView view;
|
||||
|
||||
private boolean compatibilityStart = true;
|
||||
|
||||
private boolean viewCreated = false;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
private void createView() {
|
||||
if (!viewCreated) {
|
||||
final WindowManager wm = ((WindowManager) this.getSystemService(Context.WINDOW_SERVICE));
|
||||
|
||||
final DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
|
||||
int twoThirdWidth = 2 * wm.getDefaultDisplay().getWidth() / 3;
|
||||
int twoThirdHeight = 2 * wm.getDefaultDisplay().getHeight() / 3;
|
||||
|
||||
twoThirdWidth = Math.min(twoThirdWidth, twoThirdHeight);
|
||||
twoThirdHeight = Math.max(twoThirdWidth, getHeight(twoThirdWidth));
|
||||
|
||||
final int baseWidth = Views.toPixels(dm, 300);
|
||||
final int width0 = Math.min(twoThirdWidth, baseWidth);
|
||||
final int height0 = Math.min(twoThirdHeight, getHeight(baseWidth));
|
||||
|
||||
final int width = Math.min(width0, height0);
|
||||
final int height = Math.max(width0, height0);
|
||||
|
||||
view = CalculatorOnscreenView.newInstance(this, CalculatorOnscreenViewState.newInstance(width, height, -1, -1), this);
|
||||
view.show();
|
||||
|
||||
startCalculatorListening();
|
||||
view.updateEditorState(Locator.getInstance().getEditor().getViewState());
|
||||
view.updateDisplayState(Locator.getInstance().getDisplay().getViewState());
|
||||
|
||||
viewCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
private int getHeight(int width) {
|
||||
return 4 * width / 3;
|
||||
}
|
||||
|
||||
private void startCalculatorListening() {
|
||||
Locator.getInstance().getCalculator().addCalculatorEventListener(this);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static Class<?> getIntentListenerClass() {
|
||||
return INTENT_LISTENER_CLASS;
|
||||
}
|
||||
|
||||
private void stopCalculatorListening() {
|
||||
Locator.getInstance().getCalculator().removeCalculatorEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
stopCalculatorListening();
|
||||
if (viewCreated) {
|
||||
this.view.hide();
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(Intent intent, int startId) {
|
||||
super.onStart(intent, startId);
|
||||
|
||||
if (this.compatibilityStart) {
|
||||
handleStart(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
|
||||
final int result;
|
||||
try {
|
||||
this.compatibilityStart = false;
|
||||
result = super.onStartCommand(intent, flags, startId);
|
||||
handleStart(intent);
|
||||
} finally {
|
||||
this.compatibilityStart = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void handleStart(@Nullable Intent intent) {
|
||||
if (intent != null) {
|
||||
|
||||
if (isShowWindowIntent(intent)) {
|
||||
hideNotification();
|
||||
createView();
|
||||
} else if (isShowNotificationIntent(intent)) {
|
||||
showNotification();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isShowNotificationIntent(@Nonnull Intent intent) {
|
||||
return intent.getAction().equals(SHOW_NOTIFICATION_ACTION);
|
||||
}
|
||||
|
||||
private boolean isShowWindowIntent(@Nonnull Intent intent) {
|
||||
return intent.getAction().equals(SHOW_WINDOW_ACTION);
|
||||
}
|
||||
|
||||
private void hideNotification() {
|
||||
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewMinimized() {
|
||||
showNotification();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewHidden() {
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void showNotification() {
|
||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setSmallIcon(R.drawable.kb_logo);
|
||||
builder.setContentTitle(getText(R.string.c_app_name));
|
||||
builder.setContentText(getString(R.string.open_onscreen_calculator));
|
||||
builder.setOngoing(true);
|
||||
|
||||
final Intent intent = createShowWindowIntent(this);
|
||||
builder.setContentIntent(PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.notify(NOTIFICATION_ID, builder.getNotification());
|
||||
}
|
||||
|
||||
public static void showNotification(@Nonnull Context context) {
|
||||
final Intent intent = new Intent(SHOW_NOTIFICATION_ACTION);
|
||||
intent.setClass(context, getIntentListenerClass());
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public static void showOnscreenView(@Nonnull Context context) {
|
||||
final Intent intent = createShowWindowIntent(context);
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static Intent createShowWindowIntent(@Nonnull Context context) {
|
||||
final Intent intent = new Intent(SHOW_WINDOW_ACTION);
|
||||
intent.setClass(context, getIntentListenerClass());
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCalculatorEvent(@Nonnull CalculatorEventData calculatorEventData, @Nonnull CalculatorEventType calculatorEventType, @Nullable Object data) {
|
||||
switch (calculatorEventType) {
|
||||
case editor_state_changed:
|
||||
case editor_state_changed_light:
|
||||
view.updateEditorState(((CalculatorEditorChangeEventData) data).getNewValue());
|
||||
break;
|
||||
case display_state_changed:
|
||||
view.updateDisplayState(((CalculatorDisplayChangeEventData) data).getNewValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.onscreen;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class CalculatorOnscreenStartActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
CalculatorOnscreenService.showOnscreenView(this);
|
||||
|
||||
this.finish();
|
||||
}
|
||||
}
|
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
* 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.onscreen;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import org.solovyev.android.calculator.*;
|
||||
import org.solovyev.android.prefs.Preference;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/21/12
|
||||
* Time: 9:03 PM
|
||||
*/
|
||||
public class CalculatorOnscreenView {
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* CONSTANTS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
private static final String TAG = CalculatorOnscreenView.class.getSimpleName();
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* STATIC
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
private static final Preference<CalculatorOnscreenViewState> viewStatePreference = new CalculatorOnscreenViewState.Preference("onscreen_view_state", CalculatorOnscreenViewState.newDefaultState());
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* FIELDS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
@Nonnull
|
||||
private View root;
|
||||
|
||||
@Nonnull
|
||||
private View content;
|
||||
|
||||
@Nonnull
|
||||
private View header;
|
||||
|
||||
@Nonnull
|
||||
private AndroidCalculatorEditorView editorView;
|
||||
|
||||
@Nonnull
|
||||
private AndroidCalculatorDisplayView displayView;
|
||||
|
||||
@Nonnull
|
||||
private Context context;
|
||||
|
||||
@Nonnull
|
||||
private CalculatorOnscreenViewState state = CalculatorOnscreenViewState.newDefaultState();
|
||||
|
||||
@Nullable
|
||||
private OnscreenViewListener viewListener;
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* STATES
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
private boolean minimized = false;
|
||||
|
||||
private boolean attached = false;
|
||||
|
||||
private boolean folded = false;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
private boolean hidden = true;
|
||||
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* CONSTRUCTORS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
private CalculatorOnscreenView() {
|
||||
}
|
||||
|
||||
public static CalculatorOnscreenView newInstance(@Nonnull Context context,
|
||||
@Nonnull CalculatorOnscreenViewState state,
|
||||
@Nullable OnscreenViewListener viewListener) {
|
||||
final CalculatorOnscreenView result = new CalculatorOnscreenView();
|
||||
|
||||
result.root = View.inflate(context, R.layout.onscreen_layout, null);
|
||||
result.context = context;
|
||||
result.viewListener = viewListener;
|
||||
|
||||
final CalculatorOnscreenViewState persistedState = readState(context);
|
||||
if (persistedState != null) {
|
||||
result.state = persistedState;
|
||||
} else {
|
||||
result.state = state;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* METHODS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public void updateDisplayState(@Nonnull CalculatorDisplayViewState displayState) {
|
||||
checkInit();
|
||||
displayView.setState(displayState);
|
||||
}
|
||||
|
||||
public void updateEditorState(@Nonnull CalculatorEditorViewState editorState) {
|
||||
checkInit();
|
||||
editorView.setState(editorState);
|
||||
}
|
||||
|
||||
private void setHeight(int height) {
|
||||
checkInit();
|
||||
|
||||
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams();
|
||||
|
||||
params.height = height;
|
||||
|
||||
getWindowManager().updateViewLayout(root, params);
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* LIFECYCLE
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
private void init() {
|
||||
|
||||
if (!initialized) {
|
||||
for (final CalculatorButton widgetButton : CalculatorButton.values()) {
|
||||
final View button = root.findViewById(widgetButton.getButtonId());
|
||||
if (button != null) {
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
widgetButton.onClick(context);
|
||||
if (widgetButton == CalculatorButton.app) {
|
||||
minimize();
|
||||
}
|
||||
}
|
||||
});
|
||||
button.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
widgetButton.onLongClick(context);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
header = root.findViewById(R.id.onscreen_header);
|
||||
content = root.findViewById(R.id.onscreen_content);
|
||||
|
||||
displayView = (AndroidCalculatorDisplayView) root.findViewById(R.id.calculator_display);
|
||||
displayView.init(this.context, false);
|
||||
|
||||
editorView = (AndroidCalculatorEditorView) root.findViewById(R.id.calculator_editor);
|
||||
editorView.init();
|
||||
|
||||
final View onscreenFoldButton = root.findViewById(R.id.onscreen_fold_button);
|
||||
onscreenFoldButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (folded) {
|
||||
unfold();
|
||||
} else {
|
||||
fold();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final View onscreenHideButton = root.findViewById(R.id.onscreen_minimize_button);
|
||||
onscreenHideButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
minimize();
|
||||
}
|
||||
});
|
||||
|
||||
root.findViewById(R.id.onscreen_close_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
final ImageView onscreenTitleImageView = (ImageView) root.findViewById(R.id.onscreen_title);
|
||||
onscreenTitleImageView.setOnTouchListener(new WindowDragTouchListener(wm, root));
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void checkInit() {
|
||||
if (!initialized) {
|
||||
throw new IllegalStateException("init() must be called!");
|
||||
}
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (hidden) {
|
||||
init();
|
||||
attach();
|
||||
|
||||
hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void attach() {
|
||||
checkInit();
|
||||
|
||||
final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
if (!attached) {
|
||||
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
||||
state.getWidth(),
|
||||
state.getHeight(),
|
||||
state.getX(),
|
||||
state.getY(),
|
||||
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
|
||||
params.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
|
||||
wm.addView(root, params);
|
||||
attached = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void fold() {
|
||||
if (!folded) {
|
||||
int newHeight = header.getHeight();
|
||||
content.setVisibility(View.GONE);
|
||||
setHeight(newHeight);
|
||||
folded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void unfold() {
|
||||
if (folded) {
|
||||
content.setVisibility(View.VISIBLE);
|
||||
setHeight(state.getHeight());
|
||||
folded = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void detach() {
|
||||
checkInit();
|
||||
|
||||
if (attached) {
|
||||
getWindowManager().removeView(root);
|
||||
attached = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void minimize() {
|
||||
checkInit();
|
||||
if (!minimized) {
|
||||
persistState(context, getCurrentState(!folded));
|
||||
|
||||
detach();
|
||||
|
||||
if (viewListener != null) {
|
||||
viewListener.onViewMinimized();
|
||||
}
|
||||
|
||||
minimized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void persistState(@Nonnull Context context, @Nonnull CalculatorOnscreenViewState state) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
viewStatePreference.putPreference(preferences, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CalculatorOnscreenViewState readState(@Nonnull Context context) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (viewStatePreference.isSet(preferences)) {
|
||||
return viewStatePreference.getPreference(preferences);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
checkInit();
|
||||
|
||||
if (!hidden) {
|
||||
|
||||
persistState(context, getCurrentState(!folded));
|
||||
|
||||
detach();
|
||||
|
||||
if (viewListener != null) {
|
||||
viewListener.onViewHidden();
|
||||
}
|
||||
|
||||
hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private WindowManager getWindowManager() {
|
||||
return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public CalculatorOnscreenViewState getCurrentState(boolean useRealSize) {
|
||||
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams();
|
||||
if (useRealSize) {
|
||||
return CalculatorOnscreenViewState.newInstance(params.width, params.height, params.x, params.y);
|
||||
} else {
|
||||
return CalculatorOnscreenViewState.newInstance(state.getWidth(), state.getHeight(), params.x, params.y);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* STATIC
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
private static class WindowDragTouchListener implements View.OnTouchListener {
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* CONSTANTS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
private static final float DIST_EPS = 0f;
|
||||
private static final float DIST_MAX = 100000f;
|
||||
private static final long TIME_EPS = 0L;
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* FIELDS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
@Nonnull
|
||||
private final WindowManager wm;
|
||||
|
||||
private int orientation;
|
||||
|
||||
private float x0;
|
||||
|
||||
private float y0;
|
||||
|
||||
private long time = 0;
|
||||
|
||||
@Nonnull
|
||||
private final View view;
|
||||
|
||||
private int displayWidth;
|
||||
|
||||
private int displayHeight;
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* CONSTRUCTORS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public WindowDragTouchListener(@Nonnull WindowManager wm,
|
||||
@Nonnull View view) {
|
||||
this.wm = wm;
|
||||
this.view = view;
|
||||
initDisplayParams();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (orientation != this.wm.getDefaultDisplay().getOrientation()) {
|
||||
// orientation has changed => we need to check display width/height each time window moved
|
||||
initDisplayParams();
|
||||
}
|
||||
|
||||
//Log.d(TAG, "Action: " + event.getAction());
|
||||
|
||||
final float x1 = event.getRawX();
|
||||
final float y1 = event.getRawY();
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
Log.d(TAG, "0:" + toString(x0, y0) + ", 1: " + toString(x1, y1));
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
|
||||
if (currentTime - time >= TIME_EPS) {
|
||||
time = currentTime;
|
||||
processMove(x1, y1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void initDisplayParams() {
|
||||
this.orientation = this.wm.getDefaultDisplay().getOrientation();
|
||||
|
||||
final DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
wm.getDefaultDisplay().getMetrics(displayMetrics);
|
||||
|
||||
this.displayWidth = displayMetrics.widthPixels;
|
||||
this.displayHeight = displayMetrics.heightPixels;
|
||||
}
|
||||
|
||||
private void processMove(float x1, float y1) {
|
||||
final float Δx = x1 - x0;
|
||||
final float Δy = y1 - y0;
|
||||
|
||||
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) view.getLayoutParams();
|
||||
Log.d(TAG, "0:" + toString(x0, y0) + ", 1: " + toString(x1, y1) + ", Δ: " + toString(Δx, Δy) + ", params: " + toString(params.x, params.y));
|
||||
|
||||
boolean xInBounds = isDistanceInBounds(Δx);
|
||||
boolean yInBounds = isDistanceInBounds(Δy);
|
||||
if (xInBounds || yInBounds) {
|
||||
|
||||
if (xInBounds) {
|
||||
params.x = (int) (params.x + Δx);
|
||||
}
|
||||
|
||||
if (yInBounds) {
|
||||
params.y = (int) (params.y + Δy);
|
||||
}
|
||||
|
||||
params.x = Math.min(Math.max(params.x, 0), displayWidth - params.width);
|
||||
params.y = Math.min(Math.max(params.y, 0), displayHeight - params.height);
|
||||
|
||||
wm.updateViewLayout(view, params);
|
||||
|
||||
if (xInBounds) {
|
||||
x0 = x1;
|
||||
}
|
||||
|
||||
if (yInBounds) {
|
||||
y0 = y1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDistanceInBounds(float δx) {
|
||||
δx = Math.abs(δx);
|
||||
return δx >= DIST_EPS && δx < DIST_MAX;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String toString(float x, float y) {
|
||||
return "(" + formatFloat(x) + ", " + formatFloat(y) + ")";
|
||||
}
|
||||
|
||||
private static String formatFloat(float value) {
|
||||
if (value >= 0) {
|
||||
return "+" + String.format("%.2f", value);
|
||||
} else {
|
||||
return String.format("%.2f", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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.onscreen;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.solovyev.android.prefs.AbstractPreference;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/21/12
|
||||
* Time: 10:55 PM
|
||||
*/
|
||||
public class CalculatorOnscreenViewState implements Parcelable {
|
||||
|
||||
private static final String TAG = CalculatorOnscreenViewState.class.getSimpleName();
|
||||
|
||||
public static final Parcelable.Creator<CalculatorOnscreenViewState> CREATOR = new Parcelable.Creator<CalculatorOnscreenViewState>() {
|
||||
public CalculatorOnscreenViewState createFromParcel(@Nonnull Parcel in) {
|
||||
return CalculatorOnscreenViewState.fromParcel(in);
|
||||
}
|
||||
|
||||
public CalculatorOnscreenViewState[] newArray(int size) {
|
||||
return new CalculatorOnscreenViewState[size];
|
||||
}
|
||||
};
|
||||
|
||||
private int width;
|
||||
|
||||
private int height;
|
||||
|
||||
private int x;
|
||||
|
||||
private int y;
|
||||
|
||||
private CalculatorOnscreenViewState() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static CalculatorOnscreenViewState fromParcel(@Nonnull Parcel in) {
|
||||
final CalculatorOnscreenViewState result = new CalculatorOnscreenViewState();
|
||||
result.width = in.readInt();
|
||||
result.height = in.readInt();
|
||||
result.x = in.readInt();
|
||||
result.y = in.readInt();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static CalculatorOnscreenViewState newDefaultState() {
|
||||
return newInstance(200, 400, 0, 0);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static CalculatorOnscreenViewState newInstance(int width, int height, int x, int y) {
|
||||
final CalculatorOnscreenViewState result = new CalculatorOnscreenViewState();
|
||||
result.width = width;
|
||||
result.height = height;
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(int x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(int y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@Nonnull Parcel out, int flags) {
|
||||
out.writeInt(width);
|
||||
out.writeInt(height);
|
||||
out.writeInt(x);
|
||||
out.writeInt(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CalculatorOnscreenViewState{" +
|
||||
"y=" + y +
|
||||
", x=" + x +
|
||||
", height=" + height +
|
||||
", width=" + width +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static class Preference extends AbstractPreference<CalculatorOnscreenViewState> {
|
||||
|
||||
public Preference(@Nonnull String key, @Nullable CalculatorOnscreenViewState defaultValue) {
|
||||
super(key, defaultValue);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected CalculatorOnscreenViewState getPersistedValue(@Nonnull SharedPreferences preferences) {
|
||||
try {
|
||||
final CalculatorOnscreenViewState result = new CalculatorOnscreenViewState();
|
||||
final JSONObject jsonObject = new JSONObject(preferences.getString(getKey(), "{}"));
|
||||
result.width = jsonObject.getInt("width");
|
||||
result.height = jsonObject.getInt("height");
|
||||
result.x = jsonObject.getInt("x");
|
||||
result.y = jsonObject.getInt("y");
|
||||
|
||||
Log.d(TAG, "Reading onscreen view state: " + result);
|
||||
|
||||
return result;
|
||||
} catch (JSONException e) {
|
||||
return getDefaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putPersistedValue(@Nonnull SharedPreferences.Editor editor, @Nonnull CalculatorOnscreenViewState value) {
|
||||
final Map<String, Object> properties = new HashMap<String, Object>();
|
||||
properties.put("width", value.getWidth());
|
||||
properties.put("height", value.getHeight());
|
||||
properties.put("x", value.getX());
|
||||
properties.put("y", value.getY());
|
||||
|
||||
final JSONObject jsonObject = new JSONObject(properties);
|
||||
|
||||
final String json = jsonObject.toString();
|
||||
Log.d(TAG, "Persisting onscreen view state: " + json);
|
||||
editor.putString(getKey(), json);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.onscreen;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/21/12
|
||||
* Time: 9:45 PM
|
||||
*/
|
||||
public interface OnscreenViewListener {
|
||||
|
||||
// view minimized == view is in the action bar
|
||||
void onViewMinimized();
|
||||
|
||||
// view hidden == view closed
|
||||
void onViewHidden();
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.plot;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import jscl.math.Generic;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.solovyev.android.calculator.CalculatorPreferences;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 1/12/13
|
||||
* Time: 11:03 PM
|
||||
*/
|
||||
public class AndroidCalculatorPlotter implements CalculatorPlotter, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
@Nonnull
|
||||
private final CalculatorPlotter plotter;
|
||||
|
||||
public AndroidCalculatorPlotter(@Nonnull Context context,
|
||||
@Nonnull CalculatorPlotter plotter) {
|
||||
this.plotter = plotter;
|
||||
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
|
||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
onSharedPreferenceChanged(preferences, CalculatorPreferences.Graph.plotImag.getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public PlotData getPlotData() {
|
||||
return plotter.getPlotData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addFunction(@Nonnull Generic expression) {
|
||||
return plotter.addFunction(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addFunction(@Nonnull PlotFunction plotFunction) {
|
||||
return plotter.addFunction(plotFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addFunction(@Nonnull XyFunction xyFunction) {
|
||||
return plotter.addFunction(xyFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addFunction(@Nonnull XyFunction xyFunction, @Nonnull PlotLineDef functionLineDef) {
|
||||
return plotter.addFunction(xyFunction, functionLineDef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateFunction(@Nonnull PlotFunction newFunction) {
|
||||
return plotter.updateFunction(newFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateFunction(@Nonnull XyFunction xyFunction, @Nonnull PlotLineDef functionLineDef) {
|
||||
return plotter.updateFunction(xyFunction, functionLineDef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFunction(@Nonnull PlotFunction plotFunction) {
|
||||
return plotter.removeFunction(plotFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFunction(@Nonnull XyFunction xyFunction) {
|
||||
return plotter.removeFunction(xyFunction);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PlotFunction pin(@Nonnull PlotFunction plotFunction) {
|
||||
return plotter.pin(plotFunction);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PlotFunction unpin(@Nonnull PlotFunction plotFunction) {
|
||||
return plotter.unpin(plotFunction);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PlotFunction show(@Nonnull PlotFunction plotFunction) {
|
||||
return plotter.show(plotFunction);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PlotFunction hide(@Nonnull PlotFunction plotFunction) {
|
||||
return plotter.hide(plotFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllFunctions() {
|
||||
plotter.clearAllFunctions();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PlotFunction getFunctionById(@Nonnull String functionId) {
|
||||
return plotter.getFunctionById(functionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public List<PlotFunction> getFunctions() {
|
||||
return plotter.getFunctions();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public List<PlotFunction> getVisibleFunctions() {
|
||||
return plotter.getVisibleFunctions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void plot() {
|
||||
plotter.plot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void plot(@Nonnull Generic expression) {
|
||||
plotter.plot(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean is2dPlotPossible() {
|
||||
return plotter.is2dPlotPossible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlotPossibleFor(@Nonnull Generic expression) {
|
||||
return plotter.isPlotPossibleFor(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlot3d(boolean plot3d) {
|
||||
plotter.setPlot3d(plot3d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllUnpinned() {
|
||||
plotter.removeAllUnpinned();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlotImag(boolean plotImag) {
|
||||
plotter.setPlotImag(plotImag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePlotBoundaries(@Nonnull PlotBoundaries plotBoundaries) {
|
||||
plotter.savePlotBoundaries(plotBoundaries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlotBoundaries(@Nonnull PlotBoundaries plotBoundaries) {
|
||||
plotter.setPlotBoundaries(plotBoundaries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (CalculatorPreferences.Graph.plotImag.getKey().equals(key)) {
|
||||
setPlotImag(CalculatorPreferences.Graph.plotImag.getPreference(preferences));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.plot;
|
||||
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 1/5/13
|
||||
* Time: 7:37 PM
|
||||
*/
|
||||
public enum AndroidPlotLineStyle {
|
||||
|
||||
solid(PlotLineStyle.solid) {
|
||||
@Override
|
||||
public void applyToPaint(@Nonnull Paint paint) {
|
||||
paint.setPathEffect(null);
|
||||
}
|
||||
},
|
||||
|
||||
dashed(PlotLineStyle.dashed) {
|
||||
@Override
|
||||
public void applyToPaint(@Nonnull Paint paint) {
|
||||
paint.setPathEffect(new DashPathEffect(new float[]{10, 20}, 0));
|
||||
}
|
||||
},
|
||||
|
||||
dotted(PlotLineStyle.dotted) {
|
||||
@Override
|
||||
public void applyToPaint(@Nonnull Paint paint) {
|
||||
paint.setPathEffect(new DashPathEffect(new float[]{5, 1}, 0));
|
||||
}
|
||||
},
|
||||
|
||||
dash_dotted(PlotLineStyle.dash_dotted) {
|
||||
@Override
|
||||
public void applyToPaint(@Nonnull Paint paint) {
|
||||
paint.setPathEffect(new DashPathEffect(new float[]{10, 20, 5, 1}, 0));
|
||||
}
|
||||
};
|
||||
|
||||
@Nonnull
|
||||
private final PlotLineStyle plotLineStyle;
|
||||
|
||||
AndroidPlotLineStyle(@Nonnull PlotLineStyle plotLineStyle) {
|
||||
this.plotLineStyle = plotLineStyle;
|
||||
}
|
||||
|
||||
public abstract void applyToPaint(@Nonnull Paint paint);
|
||||
|
||||
@Nullable
|
||||
public static AndroidPlotLineStyle valueOf(@Nonnull PlotLineStyle plotLineStyle) {
|
||||
for (AndroidPlotLineStyle androidPlotLineStyle : values()) {
|
||||
if (androidPlotLineStyle.plotLineStyle == plotLineStyle) {
|
||||
return androidPlotLineStyle;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Paint;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import jscl.AngleUnit;
|
||||
import org.solovyev.android.calculator.Locator;
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.view.drag.DirectionDragButton;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/22/11
|
||||
* Time: 2:42 PM
|
||||
*/
|
||||
public class AngleUnitsButton extends DirectionDragButton {
|
||||
|
||||
@Nonnull
|
||||
private AngleUnit angleUnit;
|
||||
|
||||
public AngleUnitsButton(Context context, @Nonnull AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.angleUnit = Locator.getInstance().getEngine().getAngleUnits();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDirectionTextPaint(@Nonnull Paint basePaint, @Nonnull DirectionTextData textData) {
|
||||
super.initDirectionTextPaint(basePaint, textData);
|
||||
|
||||
final String text = textData.getText();
|
||||
final TextPaint paint = textData.getPaint();
|
||||
|
||||
final int color = getDirectionTextColor(text);
|
||||
paint.setColor(color);
|
||||
if (!isCurrentAngleUnits(text)) {
|
||||
paint.setAlpha(directionTextAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
int getDirectionTextColor(@Nonnull String directionText) {
|
||||
final int color;
|
||||
final Resources resources = getResources();
|
||||
if (isCurrentAngleUnits(directionText)) {
|
||||
color = resources.getColor(R.color.cpp_selected_angle_unit_text_color);
|
||||
} else {
|
||||
color = resources.getColor(R.color.cpp_default_text_color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
boolean isCurrentAngleUnits(@Nonnull String directionText) {
|
||||
return this.angleUnit.name().equals(directionText);
|
||||
}
|
||||
|
||||
public void setAngleUnit(@Nonnull AngleUnit angleUnit) {
|
||||
if (this.angleUnit != angleUnit) {
|
||||
this.angleUnit = angleUnit;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
package org.solovyev.android.calculator.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import org.solovyev.android.calculator.CalculatorParseException;
|
||||
import org.solovyev.android.calculator.text.TextProcessor;
|
||||
import org.solovyev.android.calculator.text.TextProcessorEditorResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static org.solovyev.android.calculator.CalculatorPreferences.Gui.colorDisplay;
|
||||
import static org.solovyev.android.calculator.view.TextHighlighter.WHITE;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 6/27/13
|
||||
* Time: 6:11 PM
|
||||
*/
|
||||
public final class EditorTextProcessor implements TextProcessor<TextProcessorEditorResult, String>, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private boolean highlightText = true;
|
||||
|
||||
private final TextProcessor<TextProcessorEditorResult, String> textHighlighter = new TextHighlighter(WHITE, true);
|
||||
|
||||
public EditorTextProcessor() {
|
||||
}
|
||||
|
||||
public void init(@Nonnull Context context) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||
onSharedPreferenceChanged(preferences, colorDisplay.getKey());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public TextProcessorEditorResult process(@Nonnull String text) throws CalculatorParseException {
|
||||
TextProcessorEditorResult result;
|
||||
|
||||
if (highlightText) {
|
||||
|
||||
try {
|
||||
final TextProcessorEditorResult processesText = textHighlighter.process(text);
|
||||
|
||||
result = new TextProcessorEditorResult(Html.fromHtml(processesText.toString()), processesText.getOffset());
|
||||
} catch (CalculatorParseException e) {
|
||||
// set raw text
|
||||
result = new TextProcessorEditorResult(text, 0);
|
||||
|
||||
Log.e(this.getClass().getName(), e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
result = new TextProcessorEditorResult(text, 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isHighlightText() {
|
||||
return highlightText;
|
||||
}
|
||||
|
||||
public void setHighlightText(boolean highlightText) {
|
||||
this.highlightText = highlightText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (colorDisplay.getKey().equals(key)) {
|
||||
this.setHighlightText(colorDisplay.getPreference(preferences));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.view;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import org.solovyev.android.calculator.CalculatorParseException;
|
||||
import org.solovyev.android.calculator.Locator;
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.calculator.ToJsclTextProcessor;
|
||||
import org.solovyev.android.calculator.units.CalculatorNumeralBase;
|
||||
import org.solovyev.common.MutableObject;
|
||||
import org.solovyev.common.text.Strings;
|
||||
import org.solovyev.common.units.Unit;
|
||||
import org.solovyev.common.units.UnitImpl;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 4/22/12
|
||||
* Time: 12:20 AM
|
||||
*/
|
||||
public class NumeralBaseConverterDialog {
|
||||
|
||||
@Nullable
|
||||
private String initialFromValue;
|
||||
|
||||
public NumeralBaseConverterDialog(@Nullable String initialFromValue) {
|
||||
this.initialFromValue = initialFromValue;
|
||||
}
|
||||
|
||||
public void show(@Nonnull Context context) {
|
||||
final UnitConverterViewBuilder b = new UnitConverterViewBuilder();
|
||||
b.setFromUnitTypes(Arrays.asList(CalculatorNumeralBase.values()));
|
||||
b.setToUnitTypes(Arrays.asList(CalculatorNumeralBase.values()));
|
||||
|
||||
if (!Strings.isEmpty(initialFromValue)) {
|
||||
String value = initialFromValue;
|
||||
try {
|
||||
value = ToJsclTextProcessor.getInstance().process(value).getExpression();
|
||||
b.setFromValue(UnitImpl.newInstance(value, CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getNumeralBase())));
|
||||
} catch (CalculatorParseException e) {
|
||||
b.setFromValue(UnitImpl.newInstance(value, CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getNumeralBase())));
|
||||
}
|
||||
} else {
|
||||
b.setFromValue(UnitImpl.newInstance("", CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getNumeralBase())));
|
||||
}
|
||||
|
||||
b.setConverter(CalculatorNumeralBase.getConverter());
|
||||
|
||||
final MutableObject<AlertDialog> alertDialogHolder = new MutableObject<AlertDialog>();
|
||||
b.setOkButtonOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final AlertDialog alertDialog = alertDialogHolder.getObject();
|
||||
if (alertDialog != null) {
|
||||
alertDialog.dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
b.setCustomButtonData(new UnitConverterViewBuilder.CustomButtonData(context.getString(R.string.c_use_short), new UnitConverterViewBuilder.CustomButtonOnClickListener() {
|
||||
@Override
|
||||
public void onClick(@Nonnull Unit<String> fromUnits, @Nonnull Unit<String> toUnits) {
|
||||
String toUnitsValue = toUnits.getValue();
|
||||
|
||||
if (!toUnits.getUnitType().equals(CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getNumeralBase()))) {
|
||||
toUnitsValue = ((CalculatorNumeralBase) toUnits.getUnitType()).getNumeralBase().getJsclPrefix() + toUnitsValue;
|
||||
}
|
||||
|
||||
Locator.getInstance().getKeyboard().buttonPressed(toUnitsValue);
|
||||
final AlertDialog alertDialog = alertDialogHolder.getObject();
|
||||
if (alertDialog != null) {
|
||||
alertDialog.dismiss();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
final AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
|
||||
alertBuilder.setView(b.build(context));
|
||||
alertBuilder.setTitle(R.string.c_conversion_tool);
|
||||
|
||||
final AlertDialog alertDialog = alertBuilder.create();
|
||||
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
|
||||
lp.copyFrom(alertDialog.getWindow().getAttributes());
|
||||
|
||||
lp.width = WindowManager.LayoutParams.FILL_PARENT;
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
alertDialogHolder.setObject(alertDialog);
|
||||
alertDialog.show();
|
||||
alertDialog.getWindow().setAttributes(lp);
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import jscl.NumeralBase;
|
||||
import org.solovyev.android.calculator.Locator;
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.view.drag.DirectionDragButton;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 12/8/11
|
||||
* Time: 2:22 AM
|
||||
*/
|
||||
public class NumeralBasesButton extends DirectionDragButton {
|
||||
|
||||
@Nonnull
|
||||
private NumeralBase numeralBase;
|
||||
|
||||
public NumeralBasesButton(Context context, @Nonnull AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.numeralBase = Locator.getInstance().getEngine().getNumeralBase();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDirectionTextPaint(@Nonnull Paint basePaint, @Nonnull DirectionTextData textData) {
|
||||
super.initDirectionTextPaint(basePaint, textData);
|
||||
|
||||
final String text = textData.getText();
|
||||
final TextPaint paint = textData.getPaint();
|
||||
|
||||
final int color = getDirectionTextColor(text);
|
||||
paint.setColor(color);
|
||||
if (!isCurrentNumberBase(text)) {
|
||||
paint.setAlpha(directionTextAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
int getDirectionTextColor(@Nonnull String directionText) {
|
||||
final int color;
|
||||
if (isCurrentNumberBase(directionText)) {
|
||||
color = getResources().getColor(R.color.cpp_selected_angle_unit_text_color);
|
||||
} else {
|
||||
color = getResources().getColor(R.color.cpp_default_text_color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
boolean isCurrentNumberBase(@Nonnull String directionText) {
|
||||
return this.numeralBase.name().equals(directionText);
|
||||
}
|
||||
|
||||
public void setNumeralBase(@Nonnull NumeralBase numeralBase) {
|
||||
if (this.numeralBase != numeralBase) {
|
||||
this.numeralBase = numeralBase;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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.view;
|
||||
|
||||
import org.solovyev.android.calculator.*;
|
||||
import org.solovyev.android.calculator.math.MathType;
|
||||
import org.solovyev.android.calculator.text.TextProcessor;
|
||||
import org.solovyev.android.calculator.text.TextProcessorEditorResult;
|
||||
import org.solovyev.common.MutableObject;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 10/12/11
|
||||
* Time: 9:47 PM
|
||||
*/
|
||||
public class TextHighlighter implements TextProcessor<TextProcessorEditorResult, String> {
|
||||
|
||||
public static final int WHITE = -1;
|
||||
|
||||
private static final Map<String, String> nbFontAttributes = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
nbFontAttributes.put("color", "#008000");
|
||||
}
|
||||
|
||||
private final int color;
|
||||
private final int colorRed;
|
||||
private final int colorGreen;
|
||||
private final int colorBlue;
|
||||
private final boolean formatNumber;
|
||||
|
||||
public TextHighlighter(int baseColor, boolean formatNumber) {
|
||||
this.color = baseColor;
|
||||
this.formatNumber = formatNumber;
|
||||
//this.colorRed = Color.red(baseColor);
|
||||
this.colorRed = (baseColor >> 16) & 0xFF;
|
||||
//this.colorGreen = Color.green(baseColor);
|
||||
this.colorGreen = (color >> 8) & 0xFF;
|
||||
//this.colorBlue = Color.blue(baseColor);
|
||||
this.colorBlue = color & 0xFF;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public TextProcessorEditorResult process(@Nonnull String text) throws CalculatorParseException {
|
||||
final CharSequence result;
|
||||
|
||||
int maxNumberOfOpenGroupSymbols = 0;
|
||||
int numberOfOpenGroupSymbols = 0;
|
||||
|
||||
final StringBuilder text1 = new StringBuilder(5 * text.length());
|
||||
|
||||
int resultOffset = 0;
|
||||
|
||||
final AbstractNumberBuilder numberBuilder;
|
||||
if (!formatNumber) {
|
||||
numberBuilder = new LiteNumberBuilder(Locator.getInstance().getEngine());
|
||||
} else {
|
||||
numberBuilder = new NumberBuilder(Locator.getInstance().getEngine());
|
||||
}
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode());
|
||||
|
||||
if (numberBuilder instanceof NumberBuilder) {
|
||||
final MutableObject<Integer> numberOffset = new MutableObject<Integer>(0);
|
||||
((NumberBuilder) numberBuilder).process(text1, mathType, numberOffset);
|
||||
resultOffset += numberOffset.getObject();
|
||||
} else {
|
||||
((LiteNumberBuilder) numberBuilder).process(mathType);
|
||||
}
|
||||
|
||||
final String match = mathType.getMatch();
|
||||
switch (mathType.getMathType()) {
|
||||
case open_group_symbol:
|
||||
numberOfOpenGroupSymbols++;
|
||||
maxNumberOfOpenGroupSymbols = Math.max(maxNumberOfOpenGroupSymbols, numberOfOpenGroupSymbols);
|
||||
text1.append(text.charAt(i));
|
||||
break;
|
||||
case close_group_symbol:
|
||||
numberOfOpenGroupSymbols--;
|
||||
text1.append(text.charAt(i));
|
||||
break;
|
||||
case operator:
|
||||
text1.append(match);
|
||||
if (match.length() > 1) {
|
||||
i += match.length() - 1;
|
||||
}
|
||||
break;
|
||||
case function:
|
||||
i = processHighlightedText(text1, i, match, "i", null);
|
||||
break;
|
||||
case constant:
|
||||
i = processHighlightedText(text1, i, match, "b", null);
|
||||
break;
|
||||
case numeral_base:
|
||||
i = processHighlightedText(text1, i, match, "b", null);
|
||||
break;
|
||||
default:
|
||||
if (mathType.getMathType() == MathType.text || match.length() <= 1) {
|
||||
text1.append(text.charAt(i));
|
||||
} else {
|
||||
text1.append(match);
|
||||
i += match.length() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numberBuilder instanceof NumberBuilder) {
|
||||
final MutableObject<Integer> numberOffset = new MutableObject<Integer>(0);
|
||||
((NumberBuilder) numberBuilder).processNumber(text1, numberOffset);
|
||||
resultOffset += numberOffset.getObject();
|
||||
}
|
||||
|
||||
if (maxNumberOfOpenGroupSymbols > 0) {
|
||||
|
||||
final StringBuilder text2 = new StringBuilder(text1.length());
|
||||
|
||||
int i = processBracketGroup(text2, text1, 0, 0, maxNumberOfOpenGroupSymbols);
|
||||
for (; i < text1.length(); i++) {
|
||||
text2.append(text1.charAt(i));
|
||||
}
|
||||
|
||||
result = text2.toString();
|
||||
} else {
|
||||
result = text1.toString();
|
||||
}
|
||||
|
||||
return new TextProcessorEditorResult(result, resultOffset);
|
||||
}
|
||||
|
||||
private int processHighlightedText(@Nonnull StringBuilder result, int i, @Nonnull String match, @Nonnull String tag, @Nullable Map<String, String> tagAttributes) {
|
||||
result.append("<").append(tag);
|
||||
|
||||
if (tagAttributes != null && !tagAttributes.entrySet().isEmpty()) {
|
||||
for (Map.Entry<String, String> entry : tagAttributes.entrySet()) {
|
||||
// attr1="attr1_value" attr2="attr2_value"
|
||||
result.append(" ").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
|
||||
}
|
||||
}
|
||||
|
||||
result.append(">").append(match).append("</").append(tag).append(">");
|
||||
if (match.length() > 1) {
|
||||
return i + match.length() - 1;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
private int processBracketGroup(@Nonnull StringBuilder result, @Nonnull CharSequence s, int i, int numberOfOpenings, int maxNumberOfGroups) {
|
||||
|
||||
result.append("<font color=\"").append(getColor(maxNumberOfGroups, numberOfOpenings)).append("\">");
|
||||
|
||||
for (; i < s.length(); i++) {
|
||||
char ch = s.charAt(i);
|
||||
String strCh = String.valueOf(ch);
|
||||
|
||||
if (MathType.open_group_symbol.getTokens().contains(strCh)) {
|
||||
result.append(ch);
|
||||
result.append("</font>");
|
||||
i = processBracketGroup(result, s, i + 1, numberOfOpenings + 1, maxNumberOfGroups);
|
||||
result.append("<font color=\"").append(getColor(maxNumberOfGroups, numberOfOpenings)).append("\">");
|
||||
if (i < s.length() && MathType.close_group_symbol.getTokens().contains(String.valueOf(s.charAt(i)))) {
|
||||
result.append(s.charAt(i));
|
||||
}
|
||||
} else if (MathType.close_group_symbol.getTokens().contains(strCh)) {
|
||||
break;
|
||||
} else {
|
||||
result.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
result.append("</font>");
|
||||
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
private String getColor(int totalNumberOfOpenings, int numberOfOpenings) {
|
||||
double c = 0.8;
|
||||
|
||||
int offset = ((int) (255 * c)) * numberOfOpenings / (totalNumberOfOpenings + 1);
|
||||
|
||||
// for tests:
|
||||
// innt result = Color.rgb(BASE_COLOUR_RED_COMPONENT - offset, BASE_COLOUR_GREEN_COMPONENT - offset, BASE_COLOUR_BLUE_COMPONENT - offset);
|
||||
int result = (0xFF << 24) | ((colorRed - offset) << 16) | ((colorGreen - offset) << 8) | (colorBlue - offset);
|
||||
|
||||
return "#" + Integer.toHexString(result).substring(2);
|
||||
}
|
||||
}
|
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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.view;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.text.ClipboardManager;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.view.ViewBuilder;
|
||||
import org.solovyev.android.view.ViewFromLayoutBuilder;
|
||||
import org.solovyev.common.units.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 4/20/12
|
||||
* Time: 4:50 PM
|
||||
*/
|
||||
public class UnitConverterViewBuilder implements ViewBuilder<View> {
|
||||
|
||||
@Nonnull
|
||||
private List<? extends UnitType<String>> fromUnitTypes = Collections.emptyList();
|
||||
|
||||
@Nonnull
|
||||
private List<? extends UnitType<String>> toUnitTypes = Collections.emptyList();
|
||||
|
||||
@Nullable
|
||||
private Unit<String> fromValue;
|
||||
|
||||
@Nonnull
|
||||
private UnitConverter<String> converter = UnitConverter.Dummy.getInstance();
|
||||
|
||||
@Nullable
|
||||
private View.OnClickListener okButtonOnClickListener;
|
||||
|
||||
@Nullable
|
||||
private CustomButtonData customButtonData;
|
||||
|
||||
public void setFromUnitTypes(@Nonnull List<? extends UnitType<String>> fromUnitTypes) {
|
||||
this.fromUnitTypes = fromUnitTypes;
|
||||
}
|
||||
|
||||
public void setToUnitTypes(@Nonnull List<? extends UnitType<String>> toUnitTypes) {
|
||||
this.toUnitTypes = toUnitTypes;
|
||||
}
|
||||
|
||||
public void setFromValue(@Nullable Unit<String> fromValue) {
|
||||
this.fromValue = fromValue;
|
||||
}
|
||||
|
||||
public void setConverter(@Nonnull UnitConverter<String> converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public void setOkButtonOnClickListener(@Nullable View.OnClickListener okButtonOnClickListener) {
|
||||
this.okButtonOnClickListener = okButtonOnClickListener;
|
||||
}
|
||||
|
||||
public void setCustomButtonData(@Nullable CustomButtonData customButtonData) {
|
||||
this.customButtonData = customButtonData;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public View build(@Nonnull final Context context) {
|
||||
final View main = ViewFromLayoutBuilder.newInstance(R.layout.cpp_unit_converter).build(context);
|
||||
|
||||
final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from);
|
||||
final EditText fromEditText = (EditText) main.findViewById(R.id.units_from);
|
||||
fromEditText.addTextChangedListener(new TextWatcher() {
|
||||
@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) {
|
||||
doConversion(main, context, UnitConverterViewBuilder.this.converter);
|
||||
}
|
||||
});
|
||||
|
||||
fillSpinner(main, context, R.id.unit_types_from, fromUnitTypes);
|
||||
fillSpinner(main, context, R.id.unit_types_to, toUnitTypes);
|
||||
|
||||
if (fromValue != null) {
|
||||
fromEditText.setText(fromValue.getValue());
|
||||
|
||||
int i = fromUnitTypes.indexOf(fromValue.getUnitType());
|
||||
if (i >= 0) {
|
||||
fromSpinner.setSelection(i);
|
||||
}
|
||||
}
|
||||
|
||||
final Button copyButton = (Button) main.findViewById(R.id.unit_converter_copy_button);
|
||||
copyButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final EditText toEditText = (EditText) main.findViewById(R.id.units_to);
|
||||
|
||||
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
|
||||
clipboard.setText(toEditText.getText().toString());
|
||||
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
final Button okButton = (Button) main.findViewById(R.id.unit_converter_ok_button);
|
||||
if (okButtonOnClickListener == null) {
|
||||
((ViewGroup) okButton.getParent()).removeView(okButton);
|
||||
} else {
|
||||
okButton.setOnClickListener(this.okButtonOnClickListener);
|
||||
}
|
||||
|
||||
final Button customButton = (Button) main.findViewById(R.id.unit_converter_custom_button);
|
||||
if (customButtonData == null) {
|
||||
((ViewGroup) customButton.getParent()).removeView(customButton);
|
||||
} else {
|
||||
customButton.setText(customButtonData.text);
|
||||
customButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
customButtonData.clickListener.onClick(getFromUnit(main), getToUnit(main));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return main;
|
||||
}
|
||||
|
||||
private void fillSpinner(@Nonnull final View main,
|
||||
@Nonnull final Context context,
|
||||
final int spinnerId,
|
||||
@Nonnull List<? extends UnitType<String>> unitTypes) {
|
||||
final Spinner spinner = (Spinner) main.findViewById(spinnerId);
|
||||
|
||||
final ArrayAdapter<UnitType<String>> adapter = new ArrayAdapter<UnitType<String>>(context, android.R.layout.simple_spinner_item);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
for (UnitType<String> fromUnitType : unitTypes) {
|
||||
adapter.add(fromUnitType);
|
||||
}
|
||||
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
doConversion(main, context, UnitConverterViewBuilder.this.converter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
spinner.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private static void doConversion(@Nonnull View main, @Nonnull Context context, @Nonnull UnitConverter<String> converter) {
|
||||
final EditText fromEditText = (EditText) main.findViewById(R.id.units_from);
|
||||
|
||||
final EditText toEditText = (EditText) main.findViewById(R.id.units_to);
|
||||
|
||||
final String from = fromEditText.getText().toString();
|
||||
try {
|
||||
toEditText.setText(Conversions.doConversion(converter, from, getFromUnitType(main), getToUnitType(main)));
|
||||
} catch (ConversionException e) {
|
||||
toEditText.setText(context.getString(R.string.c_error));
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static Unit<String> getToUnit(@Nonnull View main) {
|
||||
final EditText toUnits = (EditText) main.findViewById(R.id.units_to);
|
||||
return UnitImpl.newInstance(toUnits.getText().toString(), getToUnitType(main));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static UnitType<String> getToUnitType(@Nonnull View main) {
|
||||
final Spinner toSpinner = (Spinner) main.findViewById(R.id.unit_types_to);
|
||||
return (UnitType<String>) toSpinner.getSelectedItem();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static Unit<String> getFromUnit(@Nonnull View main) {
|
||||
final EditText fromUnits = (EditText) main.findViewById(R.id.units_from);
|
||||
return UnitImpl.newInstance(fromUnits.getText().toString(), getFromUnitType(main));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static UnitType<String> getFromUnitType(@Nonnull View main) {
|
||||
final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from);
|
||||
return (UnitType<String>) fromSpinner.getSelectedItem();
|
||||
}
|
||||
|
||||
public static class CustomButtonData {
|
||||
|
||||
@Nonnull
|
||||
private String text;
|
||||
|
||||
@Nonnull
|
||||
private CustomButtonOnClickListener clickListener;
|
||||
|
||||
|
||||
public CustomButtonData(@Nonnull String text, @Nonnull CustomButtonOnClickListener clickListener) {
|
||||
this.text = text;
|
||||
this.clickListener = clickListener;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface CustomButtonOnClickListener {
|
||||
void onClick(@Nonnull Unit<String> fromUnits, @Nonnull Unit<String> toUnits);
|
||||
}
|
||||
}
|
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.widget.RemoteViews;
|
||||
import org.solovyev.android.calculator.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
|
||||
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_DISPLAY_STATE_CHANGED;
|
||||
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_EDITOR_STATE_CHANGED;
|
||||
import static org.solovyev.android.calculator.CalculatorReceiver.newButtonClickedIntent;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 19.10.12
|
||||
* Time: 16:18
|
||||
*/
|
||||
abstract class AbstractCalculatorWidgetProvider extends AppWidgetProvider {
|
||||
|
||||
private static final String TAG = "Calculator++ Widget";
|
||||
private static final int WIDGET_CATEGORY_KEYGUARD = 2;
|
||||
private static final String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
|
||||
private static final String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* FIELDS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
@Nullable
|
||||
private String cursorColor;
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* CONSTRUCTORS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
protected AbstractCalculatorWidgetProvider() {
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* METHODS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
super.onEnabled(context);
|
||||
getCursorColor(context);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String getCursorColor(@Nonnull Context context) {
|
||||
if (cursorColor == null) {
|
||||
cursorColor = Integer.toHexString(context.getResources().getColor(R.color.cpp_widget_cursor_color)).substring(2);
|
||||
}
|
||||
return cursorColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(@Nonnull Context context,
|
||||
@Nonnull AppWidgetManager appWidgetManager,
|
||||
@Nonnull int[] appWidgetIds) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds);
|
||||
|
||||
updateWidget(context, appWidgetManager, appWidgetIds);
|
||||
}
|
||||
|
||||
public void updateState(@Nonnull Context context) {
|
||||
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||
final int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, getComponentClass()));
|
||||
updateWidget(context, appWidgetManager, appWidgetIds);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected Class<? extends AbstractCalculatorWidgetProvider> getComponentClass() {
|
||||
return this.getClass();
|
||||
}
|
||||
|
||||
private void updateWidget(@Nonnull Context context,
|
||||
@Nonnull AppWidgetManager appWidgetManager,
|
||||
@Nonnull int[] appWidgetIds) {
|
||||
final CalculatorEditorViewState editorState = Locator.getInstance().getEditor().getViewState();
|
||||
final CalculatorDisplayViewState displayState = Locator.getInstance().getDisplay().getViewState();
|
||||
|
||||
final Resources resources = context.getResources();
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
final RemoteViews views = new RemoteViews(context.getPackageName(), getLayout(appWidgetManager, appWidgetId, resources));
|
||||
|
||||
for (CalculatorButton button : CalculatorButton.values()) {
|
||||
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, button.getButtonId(), newButtonClickedIntent(context, button), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
if (pendingIntent != null) {
|
||||
views.setOnClickPendingIntent(button.getButtonId(), pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
updateEditorState(context, views, editorState);
|
||||
updateDisplayState(context, views, displayState);
|
||||
|
||||
CalculatorButtons.initMultiplicationButton(views);
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
}
|
||||
|
||||
private int getLayout(@Nonnull AppWidgetManager appWidgetManager, int appWidgetId, @Nonnull Resources resources) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
final Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
|
||||
|
||||
if (options != null) {
|
||||
// Get the value of OPTION_APPWIDGET_HOST_CATEGORY
|
||||
final int category = options.getInt(OPTION_APPWIDGET_HOST_CATEGORY, -1);
|
||||
|
||||
if (category != -1) {
|
||||
// If the value is WIDGET_CATEGORY_KEYGUARD, it's a lockscreen widget
|
||||
final boolean keyguard = category == WIDGET_CATEGORY_KEYGUARD;
|
||||
if(keyguard) {
|
||||
final int minHeightDp = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, -1);
|
||||
final int minHeight = resources.getDimensionPixelSize(R.dimen.min_expanded_height_lock_screen);
|
||||
final boolean expanded = (minHeightDp >= minHeight / resources.getDisplayMetrics().density);
|
||||
if (expanded) {
|
||||
return R.layout.widget_layout_lockscreen;
|
||||
} else {
|
||||
return R.layout.widget_layout_lockscreen_collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return R.layout.widget_layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
super.onReceive(context, intent);
|
||||
|
||||
final String action = intent.getAction();
|
||||
if (ACTION_CONFIGURATION_CHANGED.equals(action)) {
|
||||
updateState(context);
|
||||
} else if (ACTION_EDITOR_STATE_CHANGED.equals(action)) {
|
||||
updateState(context);
|
||||
} else if (ACTION_DISPLAY_STATE_CHANGED.equals(action)) {
|
||||
updateState(context);
|
||||
} else if (ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
|
||||
updateState(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplayState(@Nonnull Context context, @Nonnull RemoteViews views, @Nonnull CalculatorDisplayViewState displayState) {
|
||||
if (displayState.isValid()) {
|
||||
views.setTextViewText(R.id.calculator_display, displayState.getText());
|
||||
views.setTextColor(R.id.calculator_display, context.getResources().getColor(R.color.cpp_default_text_color));
|
||||
} else {
|
||||
views.setTextColor(R.id.calculator_display, context.getResources().getColor(R.color.cpp_display_error_text_color));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateEditorState(@Nonnull Context context, @Nonnull RemoteViews views, @Nonnull CalculatorEditorViewState editorState) {
|
||||
final CharSequence text = editorState.getTextAsCharSequence();
|
||||
|
||||
CharSequence newText = text;
|
||||
int selection = editorState.getSelection();
|
||||
if (selection >= 0 && selection <= text.length()) {
|
||||
// inject cursor
|
||||
newText = Html.fromHtml(text.subSequence(0, selection) + "<font color=\"#" + getCursorColor(context) + "\">|</font>" + text.subSequence(selection, text.length()));
|
||||
}
|
||||
Locator.getInstance().getNotifier().showDebugMessage(TAG, "New editor state: " + text);
|
||||
views.setTextViewText(R.id.calculator_editor, newText);
|
||||
}
|
||||
}
|
@@ -0,0 +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.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 19.10.12
|
||||
* Time: 16:20
|
||||
*/
|
||||
public class CalculatorWidgetConfigurationActivity extends Activity {
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/18/12
|
||||
* Time: 1:00 PM
|
||||
*/
|
||||
public class CalculatorWidgetProvider extends AbstractCalculatorWidgetProvider {
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/18/12
|
||||
* Time: 1:01 PM
|
||||
*/
|
||||
public class CalculatorWidgetProvider3x4 extends AbstractCalculatorWidgetProvider {
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/18/12
|
||||
* Time: 1:01 PM
|
||||
*/
|
||||
public class CalculatorWidgetProvider4x4 extends AbstractCalculatorWidgetProvider {
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 11/18/12
|
||||
* Time: 7:43 PM
|
||||
*/
|
||||
public class CalculatorWidgetProvider4x5 extends AbstractCalculatorWidgetProvider {
|
||||
}
|
Reference in New Issue
Block a user