diff --git a/calculatorpp/AndroidManifest.xml b/calculatorpp/AndroidManifest.xml
index b03d296d..16db3a05 100644
--- a/calculatorpp/AndroidManifest.xml
+++ b/calculatorpp/AndroidManifest.xml
@@ -6,7 +6,7 @@
-
+
@@ -51,7 +51,7 @@
-
+
-
+
-
+
diff --git a/calculatorpp/res/layout/overlay_footer.xml b/calculatorpp/res/layout/onscreen_footer.xml
similarity index 100%
rename from calculatorpp/res/layout/overlay_footer.xml
rename to calculatorpp/res/layout/onscreen_footer.xml
diff --git a/calculatorpp/res/layout/onscreen_header.xml b/calculatorpp/res/layout/onscreen_header.xml
new file mode 100644
index 00000000..857cba6b
--- /dev/null
+++ b/calculatorpp/res/layout/onscreen_header.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/calculatorpp/res/layout/overlay_layout.xml b/calculatorpp/res/layout/onscreen_layout.xml
similarity index 88%
rename from calculatorpp/res/layout/overlay_layout.xml
rename to calculatorpp/res/layout/onscreen_layout.xml
index 079600ef..9b0dd823 100644
--- a/calculatorpp/res/layout/overlay_layout.xml
+++ b/calculatorpp/res/layout/onscreen_layout.xml
@@ -4,9 +4,9 @@
a:orientation="vertical"
style="@style/cpp_widget_main_layout_style">
-
+
-
@@ -39,6 +39,6 @@
a:layout_height="0dp"/>
-
+
\ No newline at end of file
diff --git a/calculatorpp/res/layout/overlay_header.xml b/calculatorpp/res/layout/overlay_header.xml
deleted file mode 100644
index a74112e8..00000000
--- a/calculatorpp/res/layout/overlay_header.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/calculatorpp/res/values-ru/text_strings.xml b/calculatorpp/res/values-ru/text_strings.xml
index 72a38b90..deb9ae6e 100644
--- a/calculatorpp/res/values-ru/text_strings.xml
+++ b/calculatorpp/res/values-ru/text_strings.xml
@@ -269,5 +269,6 @@
Калькулятор++ Виджет (3x4)
Калькулятор++ Виджет (4x4)
Калькулятор++ Виджет (4x5)
+ Нажмите чтобы открыть калькулятор поверх всех приложений
\ No newline at end of file
diff --git a/calculatorpp/res/values/dimens.xml b/calculatorpp/res/values/dimens.xml
index b9a22b58..3b70b917 100644
--- a/calculatorpp/res/values/dimens.xml
+++ b/calculatorpp/res/values/dimens.xml
@@ -25,6 +25,8 @@
25sp
6dp
+ 10dp
+
5dp
3dp
diff --git a/calculatorpp/res/values/text_strings.xml b/calculatorpp/res/values/text_strings.xml
index 546e0303..f95077c7 100644
--- a/calculatorpp/res/values/text_strings.xml
+++ b/calculatorpp/res/values/text_strings.xml
@@ -270,5 +270,6 @@
Calculator++ Widget (3x4)
Calculator++ Widget (4x4)
Calculator++ Widget (4x5)
+ Click to open on-screen calculator
\ No newline at end of file
diff --git a/calculatorpp/res/values/theme_gray.xml b/calculatorpp/res/values/theme_gray.xml
index 1ffb78ea..eefba194 100644
--- a/calculatorpp/res/values/theme_gray.xml
+++ b/calculatorpp/res/values/theme_gray.xml
@@ -86,7 +86,7 @@
- match_parent
-
diff --git a/calculatorpp/res/values/theme_metro_blue.xml b/calculatorpp/res/values/theme_metro_blue.xml
index d01df89e..80316b97 100644
--- a/calculatorpp/res/values/theme_metro_blue.xml
+++ b/calculatorpp/res/values/theme_metro_blue.xml
@@ -12,6 +12,14 @@
- @drawable/metro_button_light
+
+
diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorOnScreenStartActivity.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorOnScreenStartActivity.java
index 6e902d54..de0aa722 100644
--- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorOnScreenStartActivity.java
+++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorOnScreenStartActivity.java
@@ -3,7 +3,7 @@ package org.solovyev.android.calculator;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import org.solovyev.android.calculator.overlay.CalculatorOverlayService;
+import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService;
public class CalculatorOnScreenStartActivity extends Activity {
@@ -11,7 +11,7 @@ public class CalculatorOnScreenStartActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- startService(new Intent(this, CalculatorOverlayService.class));
+ startService(new Intent(this, CalculatorOnscreenService.class));
this.finish();
}
diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/external/AndroidExternalListenersContainer.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/external/AndroidExternalListenersContainer.java
index eab02963..a80faefc 100644
--- a/calculatorpp/src/main/java/org/solovyev/android/calculator/external/AndroidExternalListenersContainer.java
+++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/external/AndroidExternalListenersContainer.java
@@ -30,68 +30,69 @@ import java.util.Set;
*/
public class AndroidExternalListenersContainer implements CalculatorExternalListenersContainer, CalculatorEventListener {
- /*
- **********************************************************************
- *
- * CONSTANTS
- *
- **********************************************************************
- */
- public static final String EVENT_ID_EXTRA = "eventId";
- public static final String EDITOR_STATE_CHANGED_ACTION = "org.solovyev.calculator.widget.EDITOR_STATE_CHANGED";
- public static final String EDITOR_STATE_EXTRA = "editorState";
- public static final String DISPLAY_STATE_CHANGED_ACTION = "org.solovyev.calculator.widget.DISPLAY_STATE_CHANGED";
- public static final String DISPLAY_STATE_EXTRA = "displayState";
+ /*
+ **********************************************************************
+ *
+ * CONSTANTS
+ *
+ **********************************************************************
+ */
- private static final String TAG = "Calculator++ External Listener Helper";
+ public static final String EVENT_ID_EXTRA = "eventId";
+ public static final String EDITOR_STATE_CHANGED_ACTION = "org.solovyev.calculator.widget.EDITOR_STATE_CHANGED";
+ public static final String EDITOR_STATE_EXTRA = "editorState";
+ public static final String DISPLAY_STATE_CHANGED_ACTION = "org.solovyev.calculator.widget.DISPLAY_STATE_CHANGED";
+ public static final String DISPLAY_STATE_EXTRA = "displayState";
- private final Set> externalListeners = new HashSet>();
+ private static final String TAG = "Calculator++ External Listener Helper";
- @NotNull
+ private final Set> externalListeners = new HashSet>();
+
+ @NotNull
private final CalculatorEventHolder lastEvent = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
public AndroidExternalListenersContainer(@NotNull Calculator calculator) {
- calculator.addCalculatorEventListener(this);
- }
+ calculator.addCalculatorEventListener(this);
+ }
- public void onEditorStateChanged(@NotNull Context context,
- @NotNull CalculatorEventData calculatorEventData,
- @NotNull CalculatorEditorViewState editorViewState) {
+ public void onEditorStateChanged(@NotNull Context context,
+ @NotNull CalculatorEventData calculatorEventData,
+ @NotNull CalculatorEditorViewState editorViewState) {
- for (Class> externalListener : externalListeners) {
- final Intent intent = new Intent(EDITOR_STATE_CHANGED_ACTION);
- intent.setClass(context, externalListener);
- intent.putExtra(EVENT_ID_EXTRA, calculatorEventData.getEventId());
- intent.putExtra(EDITOR_STATE_EXTRA, (Parcelable) new ParcelableCalculatorEditorViewState(editorViewState));
- context.sendBroadcast(intent);
- Locator.getInstance().getNotifier().showDebugMessage(TAG, "Editor state changed broadcast sent");
- }
- }
+ for (Class> externalListener : externalListeners) {
+ final Intent intent = new Intent(EDITOR_STATE_CHANGED_ACTION);
+ intent.setClass(context, externalListener);
+ intent.putExtra(EVENT_ID_EXTRA, calculatorEventData.getEventId());
+ intent.putExtra(EDITOR_STATE_EXTRA, (Parcelable) new ParcelableCalculatorEditorViewState(editorViewState));
+ context.sendBroadcast(intent);
+ Locator.getInstance().getNotifier().showDebugMessage(TAG, "Editor state changed broadcast sent");
+ }
+ }
- private void onDisplayStateChanged(@NotNull Context context,
- @NotNull CalculatorEventData calculatorEventData,
- @NotNull CalculatorDisplayViewState displayViewState) {
- for (Class> externalListener : externalListeners) {
- final Intent intent = new Intent(DISPLAY_STATE_CHANGED_ACTION);
- intent.setClass(context, externalListener);
- intent.putExtra(EVENT_ID_EXTRA, calculatorEventData.getEventId());
- intent.putExtra(DISPLAY_STATE_EXTRA, (Parcelable) new ParcelableCalculatorDisplayViewState(displayViewState));
- context.sendBroadcast(intent);
- Locator.getInstance().getNotifier().showDebugMessage(TAG, "Display state changed broadcast sent");
- }
- }
+ private void onDisplayStateChanged(@NotNull Context context,
+ @NotNull CalculatorEventData calculatorEventData,
+ @NotNull CalculatorDisplayViewState displayViewState) {
+ for (Class> externalListener : externalListeners) {
+ final Intent intent = new Intent(DISPLAY_STATE_CHANGED_ACTION);
+ intent.setClass(context, externalListener);
+ intent.putExtra(EVENT_ID_EXTRA, calculatorEventData.getEventId());
+ intent.putExtra(DISPLAY_STATE_EXTRA, (Parcelable) new ParcelableCalculatorDisplayViewState(displayViewState));
+ context.sendBroadcast(intent);
+ Locator.getInstance().getNotifier().showDebugMessage(TAG, "Display state changed broadcast sent");
+ }
+ }
- @Override
- public void addExternalListener(@NotNull Class> externalCalculatorClass) {
- externalListeners.add(externalCalculatorClass);
- }
+ @Override
+ public void addExternalListener(@NotNull Class> externalCalculatorClass) {
+ externalListeners.add(externalCalculatorClass);
+ }
- @Override
- public boolean removeExternalListener(@NotNull Class> externalCalculatorClass) {
- return externalListeners.remove(externalCalculatorClass);
- }
+ @Override
+ public boolean removeExternalListener(@NotNull Class> externalCalculatorClass) {
+ return externalListeners.remove(externalCalculatorClass);
+ }
- @Override
+ @Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
final CalculatorEventHolder.Result result = lastEvent.apply(calculatorEventData);
if (result.isNewAfter()) {
diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/overlay/CalculatorOverlayBroadcastReceiver.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenBroadcastReceiver.java
similarity index 62%
rename from calculatorpp/src/main/java/org/solovyev/android/calculator/overlay/CalculatorOverlayBroadcastReceiver.java
rename to calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenBroadcastReceiver.java
index fa4f433f..583b128e 100644
--- a/calculatorpp/src/main/java/org/solovyev/android/calculator/overlay/CalculatorOverlayBroadcastReceiver.java
+++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenBroadcastReceiver.java
@@ -1,4 +1,4 @@
-package org.solovyev.android.calculator.overlay;
+package org.solovyev.android.calculator.onscreen;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -10,16 +10,16 @@ import org.jetbrains.annotations.NotNull;
* Date: 11/20/12
* Time: 11:05 PM
*/
-public final class CalculatorOverlayBroadcastReceiver extends BroadcastReceiver {
+public final class CalculatorOnscreenBroadcastReceiver extends BroadcastReceiver {
- public CalculatorOverlayBroadcastReceiver() {
+ public CalculatorOnscreenBroadcastReceiver() {
}
@Override
public void onReceive(@NotNull Context context,
@NotNull Intent intent) {
final Intent newIntent = new Intent(intent);
- newIntent.setClass(context, CalculatorOverlayService.class);
+ newIntent.setClass(context, CalculatorOnscreenService.class);
context.startService(newIntent);
}
}
diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenService.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenService.java
new file mode 100644
index 00000000..434121f1
--- /dev/null
+++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenService.java
@@ -0,0 +1,155 @@
+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.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.AndroidUtils;
+import org.solovyev.android.calculator.CalculatorDisplayViewState;
+import org.solovyev.android.calculator.CalculatorEditorViewState;
+import org.solovyev.android.calculator.Locator;
+import org.solovyev.android.calculator.R;
+import org.solovyev.android.calculator.external.DefaultExternalCalculatorIntentHandler;
+import org.solovyev.android.calculator.external.ExternalCalculatorIntentHandler;
+import org.solovyev.android.calculator.external.ExternalCalculatorStateUpdater;
+
+/**
+ * User: serso
+ * Date: 11/20/12
+ * Time: 9:42 PM
+ */
+public class CalculatorOnscreenService extends Service implements ExternalCalculatorStateUpdater, OnscreenViewListener {
+
+ private static final int NOTIFICATION_ID = 9031988; // my birthday =)
+
+ @NotNull
+ private final ExternalCalculatorIntentHandler intentHandler = new DefaultExternalCalculatorIntentHandler(this);
+
+ @Nullable
+ private static String cursorColor;
+
+ @NotNull
+ private CalculatorOnscreenView view;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ final WindowManager wm = ((WindowManager) this.getSystemService(Context.WINDOW_SERVICE));
+
+ final DisplayMetrics dm = getResources().getDisplayMetrics();
+
+ final int displayWidth0 = 2 * wm.getDefaultDisplay().getWidth() / 3;
+ final int displayHeight0 = 2 * wm.getDefaultDisplay().getHeight() / 3;
+
+ final int displayWidth = Math.min(displayWidth0, displayHeight0);
+ final int displayHeight = Math.max(displayWidth0, displayHeight0);
+
+ final int width0 = Math.min(displayWidth, AndroidUtils.toPixels(dm, 300));
+ final int height0 = Math.min(displayHeight, AndroidUtils.toPixels(dm, 450));
+
+ final int width = Math.min(width0, height0);
+ final int height = Math.max(width0, height0);
+
+ view = CalculatorOnscreenView.newInstance(this, CalculatorOnscreenViewDef.newInstance(width, height, -1, -1), getCursorColor(this), this);
+ view.show();
+
+ startCalculatorListening();
+ }
+
+ private void startCalculatorListening() {
+ Locator.getInstance().getExternalListenersContainer().addExternalListener(getIntentListenerClass());
+ }
+
+ @NotNull
+ private Class> getIntentListenerClass() {
+ return CalculatorOnscreenBroadcastReceiver.class;
+ }
+
+ private void stopCalculatorListening() {
+ Locator.getInstance().getExternalListenersContainer().removeExternalListener(getIntentListenerClass());
+ }
+
+ @Override
+ public void onDestroy() {
+ stopCalculatorListening();
+ this.view.hide();
+ super.onDestroy();
+ }
+
+ @Override
+ public void updateState(@NotNull Context context, @NotNull CalculatorEditorViewState editorState, @NotNull CalculatorDisplayViewState displayState) {
+ view.updateDisplayState(displayState);
+ view.updateEditorState(editorState);
+ }
+
+ @NotNull
+ private static String getCursorColor(@NotNull Context context) {
+ if (cursorColor == null) {
+ cursorColor = Integer.toHexString(context.getResources().getColor(R.color.cpp_widget_cursor_color)).substring(2);
+ }
+ return cursorColor;
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ int result = super.onStartCommand(intent, flags, startId);
+
+ if ( intent != null ) {
+ intentHandler.onIntent(this, intent);
+ }
+
+ hideNotification();
+
+
+ return result;
+ }
+
+ 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));
+
+ final Intent intent = new Intent();
+ intent.setClass(this, getIntentListenerClass());
+ 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());
+ }
+}
+
diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenView.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenView.java
new file mode 100644
index 00000000..5b6e527c
--- /dev/null
+++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenView.java
@@ -0,0 +1,393 @@
+package org.solovyev.android.calculator.onscreen;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.text.Html;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.calculator.CalculatorDisplayViewState;
+import org.solovyev.android.calculator.CalculatorEditorViewState;
+import org.solovyev.android.calculator.R;
+import org.solovyev.android.calculator.widget.WidgetButton;
+
+/**
+ * User: serso
+ * Date: 11/21/12
+ * Time: 9:03 PM
+ */
+public class CalculatorOnscreenView {
+ /*
+ **********************************************************************
+ *
+ * CONSTANTS
+ *
+ **********************************************************************
+ */
+
+ private static final String TAG = CalculatorOnscreenView.class.getSimpleName();
+
+ /*
+ **********************************************************************
+ *
+ * FIELDS
+ *
+ **********************************************************************
+ */
+
+ @NotNull
+ private View root;
+
+ @NotNull
+ private View content;
+
+ @NotNull
+ private Context context;
+
+ private int width;
+
+ private int height;
+
+ private int unfoldedHeight;
+
+ @NotNull
+ private String cursorColor;
+
+ @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(@NotNull Context context,
+ @NotNull CalculatorOnscreenViewDef def,
+ @NotNull String cursorColor,
+ @Nullable OnscreenViewListener viewListener) {
+ final CalculatorOnscreenView result = new CalculatorOnscreenView();
+
+ result.root = View.inflate(context, R.layout.onscreen_layout, null);
+ result.context = context;
+ result.width = def.getWidth();
+ result.height = def.getHeight();
+ result.cursorColor = cursorColor;
+ result.viewListener = viewListener;
+
+ return result;
+ }
+
+ /*
+ **********************************************************************
+ *
+ * METHODS
+ *
+ **********************************************************************
+ */
+
+ public void updateDisplayState(@NotNull CalculatorDisplayViewState displayState) {
+ checkInit();
+
+ final TextView calculatorDisplayView = (TextView) root.findViewById(R.id.calculator_display);
+ if (calculatorDisplayView != null) {
+ if (displayState.isValid()) {
+ calculatorDisplayView.setText(displayState.getText());
+ calculatorDisplayView.setTextColor(context.getResources().getColor(R.color.cpp_default_text_color));
+ } else {
+ calculatorDisplayView.setTextColor(context.getResources().getColor(R.color.cpp_display_error_text_color));
+ }
+ }
+ }
+
+ public void updateEditorState(@NotNull CalculatorEditorViewState editorState) {
+ checkInit();
+ final TextView calculatorEditorView = (TextView) root.findViewById(R.id.calculator_editor);
+
+ if (calculatorEditorView != null) {
+ String text = editorState.getText();
+
+ CharSequence newText = text;
+ int selection = editorState.getSelection();
+ if (selection >= 0 && selection <= text.length()) {
+ // inject cursor
+ newText = Html.fromHtml(text.substring(0, selection) + "|" + text.substring(selection));
+ }
+ calculatorEditorView.setText(newText);
+ }
+ }
+
+ private void setHeight(int height) {
+ checkInit();
+
+ this.height = height;
+
+ final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams();
+
+ params.height = height;
+
+ getWindowManager().updateViewLayout(root, params);
+ }
+
+ /*
+ **********************************************************************
+ *
+ * LIFECYCLE
+ *
+ **********************************************************************
+ */
+
+ private void init() {
+
+ if (!initialized) {
+ for (final WidgetButton widgetButton : WidgetButton.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);
+ }
+ });
+ }
+ }
+
+ final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+
+ content = root.findViewById(R.id.onscreen_content);
+
+ 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 TextView onscreenTitleTextView = (TextView) root.findViewById(R.id.onscreen_title);
+ onscreenTitleTextView.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(
+ width,
+ height,
+ WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+ PixelFormat.TRANSLUCENT);
+
+ wm.addView(root, params);
+ attached = true;
+ }
+ }
+
+ private void fold() {
+ if (!folded) {
+ final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams();
+ unfoldedHeight = params.height;
+ int newHeight = root.findViewById(R.id.onscreen_close_button).getHeight();
+ content.setVisibility(View.GONE);
+ setHeight(newHeight);
+ folded = true;
+ }
+ }
+
+ private void unfold() {
+ if (folded) {
+ content.setVisibility(View.VISIBLE);
+ setHeight(unfoldedHeight);
+ folded = false;
+ }
+ }
+
+ public void detach() {
+ checkInit();
+
+ if (attached) {
+ getWindowManager().removeView(root);
+ attached = false;
+ }
+ }
+
+ public void minimize() {
+ checkInit();
+ if (!minimized) {
+ detach();
+
+ if (viewListener != null) {
+ viewListener.onViewMinimized();
+ }
+
+ minimized = true;
+ }
+ }
+
+ public void hide() {
+ checkInit();
+
+ if (!hidden) {
+
+ detach();
+
+ if (viewListener != null) {
+ viewListener.onViewHidden();
+ }
+
+ hidden = true;
+ }
+ }
+
+ @NotNull
+ private WindowManager getWindowManager() {
+ return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
+ }
+
+ @NotNull
+ public CalculatorOnscreenViewDef getCalculatorOnscreenViewDef() {
+ final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams();
+ return CalculatorOnscreenViewDef.newInstance(width, height, params.x, params.y);
+ }
+
+ /*
+ **********************************************************************
+ *
+ * STATIC
+ *
+ **********************************************************************
+ */
+
+ private static class WindowDragTouchListener implements View.OnTouchListener {
+
+ private final WindowManager wm;
+
+ private float x0;
+
+ private float y0;
+
+ @NotNull
+ private final View view;
+
+ public WindowDragTouchListener(@NotNull WindowManager wm,
+ @NotNull View view) {
+ this.wm = wm;
+ this.view = view;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+
+ //Log.d(TAG, "Action: " + event.getAction());
+
+ final float x1 = event.getX();
+ final float y1 = event.getY();
+
+ 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 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));
+
+ params.x = (int) (params.x + Δx);
+ params.y = (int) (params.y + Δy);
+
+ wm.updateViewLayout(view, params);
+ x0 = x1;
+ y0 = y1;
+ return true;
+ }
+
+ return false;
+ }
+
+ @NotNull
+ 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);
+ }
+ }
+ }
+}
diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenViewDef.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenViewDef.java
new file mode 100644
index 00000000..accffb33
--- /dev/null
+++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/CalculatorOnscreenViewDef.java
@@ -0,0 +1,61 @@
+package org.solovyev.android.calculator.onscreen;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: serso
+ * Date: 11/21/12
+ * Time: 10:55 PM
+ */
+public class CalculatorOnscreenViewDef {
+
+ private int width;
+ private int height;
+ private int x;
+ private int y;
+
+ private CalculatorOnscreenViewDef() {
+ }
+
+ @NotNull
+ public static CalculatorOnscreenViewDef newInstance(int width, int height, int x, int y) {
+ final CalculatorOnscreenViewDef result = new CalculatorOnscreenViewDef();
+ 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;
+ }
+}
diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/OnscreenViewListener.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/OnscreenViewListener.java
new file mode 100644
index 00000000..d5d9bc17
--- /dev/null
+++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/onscreen/OnscreenViewListener.java
@@ -0,0 +1,15 @@
+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();
+}
diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/overlay/CalculatorOverlayService.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/overlay/CalculatorOverlayService.java
deleted file mode 100644
index e6fbea27..00000000
--- a/calculatorpp/src/main/java/org/solovyev/android/calculator/overlay/CalculatorOverlayService.java
+++ /dev/null
@@ -1,253 +0,0 @@
-package org.solovyev.android.calculator.overlay;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.os.IBinder;
-import android.text.Html;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.TextView;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.solovyev.android.calculator.CalculatorDisplayViewState;
-import org.solovyev.android.calculator.CalculatorEditorViewState;
-import org.solovyev.android.calculator.Locator;
-import org.solovyev.android.calculator.R;
-import org.solovyev.android.calculator.external.DefaultExternalCalculatorIntentHandler;
-import org.solovyev.android.calculator.external.ExternalCalculatorIntentHandler;
-import org.solovyev.android.calculator.external.ExternalCalculatorStateUpdater;
-import org.solovyev.android.calculator.widget.WidgetButton;
-
-/**
- * User: serso
- * Date: 11/20/12
- * Time: 9:42 PM
- */
-public class CalculatorOverlayService extends Service implements ExternalCalculatorStateUpdater {
-
- @Nullable
- private View onscreenView;
-
- @NotNull
- private final ExternalCalculatorIntentHandler intentHandler = new DefaultExternalCalculatorIntentHandler(this);
-
- @Nullable
- private static String cursorColor;
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- final WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
-
- final LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- onscreenView = layoutInflater.inflate(R.layout.overlay_layout, null);
-
- for (final WidgetButton widgetButton : WidgetButton.values()) {
- final View button = onscreenView.findViewById(widgetButton.getButtonId());
- if (button != null) {
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- widgetButton.onClick(CalculatorOverlayService.this);
- }
- });
- }
- }
-
-
- final int initialWindowWidth = Math.max(wm.getDefaultDisplay().getWidth() / 2, 300);
- final int initialWindowHeight = Math.max(wm.getDefaultDisplay().getHeight() / 2, 450);
-
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- initialWindowWidth,
- initialWindowHeight,
- WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
- PixelFormat.TRANSLUCENT);
-
- final View overlayContent = onscreenView.findViewById(R.id.overlay_content);
- final View overlayHideButton = onscreenView.findViewById(R.id.overlay_hide_button);
- overlayHideButton.setOnClickListener(new View.OnClickListener() {
-
- private boolean hidden = false;
-
- private int windowHeight = initialWindowHeight;
-
- @Override
- public void onClick(View v) {
- final WindowManager.LayoutParams params = (WindowManager.LayoutParams) onscreenView.getLayoutParams();
- if (hidden) {
- overlayContent.setVisibility(View.VISIBLE);
- params.height = windowHeight;
- } else {
- windowHeight = params.height;
- overlayContent.setVisibility(View.GONE);
- params.height = overlayHideButton.getHeight();
- }
-
- wm.updateViewLayout(onscreenView, params);
-
- hidden = !hidden;
- }
- });
-
-
- onscreenView.findViewById(R.id.overlay_close_button).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- stopService(new Intent(getApplicationContext(), CalculatorOverlayService.class));
- }
- });
-
- final TextView overlayTitleTextView = (TextView) onscreenView.findViewById(R.id.overlay_title);
- overlayTitleTextView.setOnTouchListener(new View.OnTouchListener() {
-
- private boolean move = true;
-
- private float x0;
-
- private float y0;
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE:
-
- if (move) {
- float xOffset = event.getX() - x0;
- float yOffset = event.getY() - y0;
-
- final WindowManager.LayoutParams params = (WindowManager.LayoutParams) onscreenView.getLayoutParams();
-
- int newX = (int) (params.x + xOffset);
- int newY = (int) (params.y + yOffset);
-
- params.x = newX;
- params.y = newY;
- wm.updateViewLayout(onscreenView, params);
-
- if (newX != params.x) {
- x0 = event.getX();
- }
-
- if (newY != params.y) {
- y0 = event.getY();
- }
- } else {
- move = true;
- x0 = event.getX();
- y0 = event.getY();
- }
-
- return true;
- }
-
- move = false;
-
- return false;
- }
- });
-
- wm.addView(onscreenView, params);
-
- startCalculatorListening();
- }
-
- private void startCalculatorListening() {
- Locator.getInstance().getExternalListenersContainer().addExternalListener(getIntentListenerClass());
- }
-
- @NotNull
- private Class> getIntentListenerClass() {
- return CalculatorOverlayBroadcastReceiver.class;
- }
-
- private void stopCalculatorListening() {
- Locator.getInstance().getExternalListenersContainer().removeExternalListener(getIntentListenerClass());
- }
-
- @Override
- public void onDestroy() {
- stopCalculatorListening();
-
- if (onscreenView != null) {
- ((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(onscreenView);
- onscreenView = null;
- }
-
- super.onDestroy();
- }
-
- @Override
- public void updateState(@NotNull Context context, @NotNull CalculatorEditorViewState editorState, @NotNull CalculatorDisplayViewState displayState) {
- final View root = this.onscreenView;
- if (root != null) {
- updateDisplayState(context, root, displayState);
- updateEditorState(context, root, editorState);
- }
- }
-
- private static void updateDisplayState(@NotNull Context context, @NotNull View root, @NotNull CalculatorDisplayViewState displayState) {
- final TextView calculatorDisplayView = (TextView) root.findViewById(R.id.calculator_display);
- if (calculatorDisplayView != null) {
- if (displayState.isValid()) {
- calculatorDisplayView.setText(displayState.getText());
- calculatorDisplayView.setTextColor(context.getResources().getColor(R.color.cpp_default_text_color));
- } else {
- calculatorDisplayView.setTextColor(context.getResources().getColor(R.color.cpp_display_error_text_color));
- }
- }
- }
-
- private static void updateEditorState(@NotNull Context context, @NotNull View root, @NotNull CalculatorEditorViewState editorState) {
- final TextView calculatorEditorView = (TextView) root.findViewById(R.id.calculator_editor);
-
- if (calculatorEditorView != null) {
- String text = editorState.getText();
-
- CharSequence newText = text;
- int selection = editorState.getSelection();
- if (selection >= 0 && selection <= text.length()) {
- // inject cursor
- newText = Html.fromHtml(text.substring(0, selection) + "|" + text.substring(selection));
- }
- calculatorEditorView.setText(newText);
- }
- }
-
- @NotNull
- private static String getCursorColor(@NotNull Context context) {
- if (cursorColor == null) {
- cursorColor = Integer.toHexString(context.getResources().getColor(R.color.cpp_widget_cursor_color)).substring(2);
- }
- return cursorColor;
- }
-
- @Override
- public void onStart(Intent intent, int startId) {
- super.onStart(intent, startId);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- int result = super.onStartCommand(intent, flags, startId);
-
- if ( intent != null ) {
- intentHandler.onIntent(this, intent);
- }
-
- return result;
- }
-}
-