Floating calculator refactor + dedicated preferences file for it

This commit is contained in:
serso 2016-02-17 11:45:55 +01:00
parent 8c897ba441
commit 95b22cd253
4 changed files with 284 additions and 420 deletions

View File

@ -1,6 +1,7 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.os.Handler; import android.os.Handler;
@ -8,18 +9,23 @@ import android.os.Looper;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import com.squareup.otto.GeneratedHandlerFinder; import com.squareup.otto.GeneratedHandlerFinder;
import org.solovyev.android.UiThreadExecutor;
import org.solovyev.android.checkout.Billing;
import org.solovyev.android.checkout.Checkout;
import org.solovyev.android.checkout.Inventory;
import org.solovyev.android.checkout.ProductTypes;
import org.solovyev.android.checkout.Products;
import org.solovyev.android.checkout.RobotmediaDatabase;
import org.solovyev.android.checkout.RobotmediaInventory;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import jscl.JsclMathEngine; import jscl.JsclMathEngine;
import org.solovyev.android.UiThreadExecutor;
import org.solovyev.android.checkout.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File; import java.io.File;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -27,6 +33,11 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.inject.Singleton;
@Module @Module
public class AppModule { public class AppModule {
@ -37,6 +48,7 @@ public class AppModule {
// multiple threads // multiple threads
public static final String THREAD_BACKGROUND = "thread-background"; public static final String THREAD_BACKGROUND = "thread-background";
public static final String DIR_FILES = "dir-files"; public static final String DIR_FILES = "dir-files";
public static final String PREFS_FLOATING = "prefs-floating";
@NonNull @NonNull
private final Application application; private final Application application;
@ -69,6 +81,13 @@ public class AppModule {
return PreferenceManager.getDefaultSharedPreferences(application); return PreferenceManager.getDefaultSharedPreferences(application);
} }
@Provides
@Singleton
@Named(PREFS_FLOATING)
SharedPreferences provideFloatingPreferences() {
return application.getSharedPreferences("floating-calculator", Context.MODE_PRIVATE);
}
@Provides @Provides
@Singleton @Singleton
@Named(THREAD_INIT) @Named(THREAD_INIT)

View File

@ -94,25 +94,18 @@ public class FloatingCalculatorService extends Service implements FloatingViewLi
return; return;
} }
final WindowManager wm = ((WindowManager) this.getSystemService(Context.WINDOW_SERVICE)); final WindowManager wm = ((WindowManager) this.getSystemService(Context.WINDOW_SERVICE));
final DisplayMetrics dm = getResources().getDisplayMetrics(); final DisplayMetrics dm = getResources().getDisplayMetrics();
final android.view.Display dd = wm.getDefaultDisplay();
int twoThirdWidth = 2 * wm.getDefaultDisplay().getWidth() / 3; //noinspection deprecation
int twoThirdHeight = 2 * wm.getDefaultDisplay().getHeight() / 3; final int maxWidth = 2 * Math.min(dd.getWidth(), dd.getHeight()) / 3;
final int desiredWidth = Views.toPixels(dm, 300);
twoThirdWidth = Math.min(twoThirdWidth, twoThirdHeight); final int width = Math.min(maxWidth, desiredWidth);
twoThirdHeight = Math.max(twoThirdWidth, getHeight(twoThirdWidth)); final int height = getHeight(width);
final int baseWidth = Views.toPixels(dm, 300); final FloatingCalculatorView.State state = new FloatingCalculatorView.State(width, height, -1, -1);
final int width0 = Math.min(twoThirdWidth, baseWidth); view = new FloatingCalculatorView(this, state, this);
final int height0 = Math.min(twoThirdHeight, getHeight(baseWidth));
final int width = Math.min(width0, height0);
final int height = Math.max(width0, height0);
view = FloatingCalculatorView
.create(this, FloatingCalculatorViewState.create(width, height, -1, -1), this,
preferences);
view.show(); view.show();
view.updateEditorState(editor.getState()); view.updateEditorState(editor.getState());
view.updateDisplayState(display.getState()); view.updateDisplayState(display.getState());

View File

@ -33,16 +33,18 @@ import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager; import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.view.Display;
import android.view.Gravity; import android.view.Gravity;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.ImageView; import android.widget.ImageView;
import org.solovyev.android.calculator.AppModule;
import org.solovyev.android.calculator.DisplayState; import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.DisplayView; import org.solovyev.android.calculator.DisplayView;
import org.solovyev.android.calculator.Editor; import org.solovyev.android.calculator.Editor;
@ -52,20 +54,199 @@ import org.solovyev.android.calculator.Keyboard;
import org.solovyev.android.calculator.Preferences; import org.solovyev.android.calculator.Preferences;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.buttons.CppButton; import org.solovyev.android.calculator.buttons.CppButton;
import org.solovyev.android.prefs.Preference;
import java.util.Locale;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
public class FloatingCalculatorView { public class FloatingCalculatorView {
private static final String TAG = FloatingCalculatorView.class.getSimpleName();
private static final Preference<FloatingCalculatorViewState> viewStatePreference = new FloatingCalculatorViewState.Preference("onscreen_view_state", FloatingCalculatorViewState private static class MyTouchListener implements View.OnTouchListener {
.createDefault()); private static final float DIST_EPS = 0f;
private static final float DIST_MAX = 100000f;
private static final long TIME_EPS = 0L;
@Nonnull
private final WindowManager wm;
@Nonnull
private final View view;
private int orientation;
private float x0;
private float y0;
private long lastMoveTime = 0;
private final DisplayMetrics dm = new DisplayMetrics();
public MyTouchListener(@Nonnull WindowManager wm,
@Nonnull View view) {
this.wm = wm;
this.view = view;
onDisplayChanged();
}
private void onDisplayChanged() {
final Display dd = wm.getDefaultDisplay();
//noinspection deprecation
orientation = dd.getOrientation();
dd.getMetrics(dm);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
//noinspection deprecation
if (orientation != wm.getDefaultDisplay().getOrientation()) {
// orientation has changed => we need to check display width/height each time window moved
onDisplayChanged();
}
final float x1 = event.getRawX();
final float y1 = event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x0 = x1;
y0 = y1;
return true;
case MotionEvent.ACTION_MOVE:
final long now = System.currentTimeMillis();
if (now - lastMoveTime >= TIME_EPS) {
lastMoveTime = now;
processMove(x1, y1);
}
return true;
}
return false;
}
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();
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), dm.widthPixels - params.width);
params.y = Math.min(Math.max(params.y, 0), dm.heightPixels - 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;
}
}
public static class State implements Parcelable {
public static final Creator<State> CREATOR = new Creator<State>() {
public State createFromParcel(@Nonnull Parcel in) {
return new State(in);
}
public State[] newArray(int size) {
return new State[size];
}
};
public final int width;
public final int height;
public final int x;
public final int y;
public State(int width, int height, int x, int y) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
private State(@NonNull SharedPreferences prefs) {
width = prefs.getInt("width", 200);
height = prefs.getInt("height", 400);
x = prefs.getInt("x", 0);
y = prefs.getInt("y", 0);
}
public State(@Nonnull Parcel in) {
width = in.readInt();
height = in.readInt();
x = in.readInt();
y = in.readInt();
}
@android.support.annotation.Nullable
public static State fromPrefs(@NonNull SharedPreferences prefs) {
if(!prefs.contains("width")) {
return null;
}
return new State(prefs);
}
@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 "State{" +
"y=" + y +
", x=" + x +
", height=" + height +
", width=" + width +
'}';
}
public void save(@NonNull SharedPreferences.Editor editor) {
editor.putInt("width", width);
editor.putInt("height", height);
editor.putInt("x", x);
editor.putInt("y", y);
}
}
@NonNull
private final Context context;
@NonNull
private final FloatingViewListener listener;
@Inject
Keyboard keyboard;
@Inject
Editor editor;
@Inject
SharedPreferences preferences;
@Named(AppModule.PREFS_FLOATING)
@Inject
SharedPreferences myPreferences;
private View root; private View root;
private View content; private View content;
private View header; private View header;
@ -73,61 +254,30 @@ public class FloatingCalculatorView {
private Drawable headerTitleDrawable; private Drawable headerTitleDrawable;
private EditorView editorView; private EditorView editorView;
private DisplayView displayView; private DisplayView displayView;
private Context context;
@Nonnull @Nonnull
private FloatingCalculatorViewState state = FloatingCalculatorViewState.createDefault(); private final State state;
@Nullable
private FloatingViewListener viewListener;
@Inject
Keyboard keyboard;
@Inject
Editor editor;
private boolean minimized; private boolean minimized;
private boolean attached; private boolean attached;
private boolean folded; private boolean folded;
private boolean initialized; private boolean initialized;
private boolean shown; private boolean shown;
public FloatingCalculatorView(@Nonnull Context context,
private FloatingCalculatorView() { @Nonnull State state,
} @NonNull FloatingViewListener listener) {
cast(context).getComponent().inject(this);
public static FloatingCalculatorView create(@Nonnull Context context, this.context = context;
@Nonnull FloatingCalculatorViewState state, this.listener = listener;
@Nullable FloatingViewListener viewListener, final Preferences.SimpleTheme theme =
@NonNull SharedPreferences preferences) { Preferences.Onscreen.theme.getPreferenceNoError(preferences);
final FloatingCalculatorView view = new FloatingCalculatorView(); final Preferences.Gui.Theme appTheme =
cast(context).getComponent().inject(view); Preferences.Gui.theme.getPreferenceNoError(preferences);
final Preferences.SimpleTheme theme = Preferences.Onscreen.theme.getPreferenceNoError(preferences); this.root = View.inflate(context, theme.getOnscreenLayout(appTheme), null);
final Preferences.Gui.Theme appTheme = Preferences.Gui.theme.getPreferenceNoError(preferences); final State persistedState = State.fromPrefs(myPreferences);
view.root = View.inflate(context, theme.getOnscreenLayout(appTheme), null);
view.context = context;
view.viewListener = viewListener;
final FloatingCalculatorViewState persistedState = readState(context);
if (persistedState != null) { if (persistedState != null) {
view.state = persistedState; this.state = persistedState;
} else { } else {
view.state = state; this.state = state;
}
return view;
}
public static void persistState(@Nonnull Context context, @Nonnull
FloatingCalculatorViewState state) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
viewStatePreference.putPreference(preferences, state);
}
@Nullable
public static FloatingCalculatorViewState readState(@Nonnull Context context) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (viewStatePreference.isSet(preferences)) {
return viewStatePreference.getPreference(preferences);
} else {
return null;
} }
} }
@ -136,6 +286,12 @@ public class FloatingCalculatorView {
displayView.setState(displayState); displayView.setState(displayState);
} }
private void checkInit() {
if (!initialized) {
throw new IllegalStateException("init() must be called!");
}
}
public void updateEditorState(@Nonnull EditorState editorState) { public void updateEditorState(@Nonnull EditorState editorState) {
checkInit(); checkInit();
editorView.setState(editorState); editorView.setState(editorState);
@ -144,7 +300,8 @@ public class FloatingCalculatorView {
private void setHeight(int height) { private void setHeight(int height) {
checkInit(); checkInit();
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams(); final WindowManager.LayoutParams params =
(WindowManager.LayoutParams) root.getLayoutParams();
params.height = height; params.height = height;
getWindowManager().updateViewLayout(root, params); getWindowManager().updateViewLayout(root, params);
} }
@ -164,7 +321,8 @@ public class FloatingCalculatorView {
public void onClick(View v) { public void onClick(View v) {
if (keyboard.buttonPressed(widgetButton.action)) { if (keyboard.buttonPressed(widgetButton.action)) {
if (keyboard.isVibrateOnKeypress()) { if (keyboard.isVibrateOnKeypress()) {
v.performHapticFeedback(KEYBOARD_TAP, FLAG_IGNORE_GLOBAL_SETTING | FLAG_IGNORE_VIEW_SETTING); v.performHapticFeedback(KEYBOARD_TAP,
FLAG_IGNORE_GLOBAL_SETTING | FLAG_IGNORE_VIEW_SETTING);
} }
} }
if (widgetButton == CppButton.app) { if (widgetButton == CppButton.app) {
@ -177,7 +335,8 @@ public class FloatingCalculatorView {
public boolean onLongClick(View v) { public boolean onLongClick(View v) {
if (keyboard.buttonPressed(widgetButton.actionLong)) { if (keyboard.buttonPressed(widgetButton.actionLong)) {
if (keyboard.isVibrateOnKeypress()) { if (keyboard.isVibrateOnKeypress()) {
v.performHapticFeedback(LONG_PRESS, FLAG_IGNORE_GLOBAL_SETTING | FLAG_IGNORE_VIEW_SETTING); v.performHapticFeedback(LONG_PRESS,
FLAG_IGNORE_GLOBAL_SETTING | FLAG_IGNORE_VIEW_SETTING);
} }
} }
return true; return true;
@ -218,25 +377,20 @@ public class FloatingCalculatorView {
} }
}); });
root.findViewById(R.id.onscreen_close_button).setOnClickListener(new View.OnClickListener() { root.findViewById(R.id.onscreen_close_button)
@Override .setOnClickListener(new View.OnClickListener() {
public void onClick(View v) { @Override
hide(); public void onClick(View v) {
} hide();
}); }
});
headerTitle.setOnTouchListener(new WindowDragTouchListener(wm, root)); headerTitle.setOnTouchListener(new MyTouchListener(wm, root));
initialized = true; initialized = true;
} }
private void checkInit() {
if (!initialized) {
throw new IllegalStateException("init() must be called!");
}
}
public void show() { public void show() {
if (shown) { if (shown) {
return; return;
@ -253,12 +407,14 @@ public class FloatingCalculatorView {
final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (!attached) { if (!attached) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams( final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
state.getWidth(), state.width,
state.getHeight(), state.height,
state.getX(), state.x,
state.getY(), state.y,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT); PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.LEFT; params.gravity = Gravity.TOP | Gravity.LEFT;
@ -272,7 +428,8 @@ public class FloatingCalculatorView {
if (!folded) { if (!folded) {
headerTitle.setImageDrawable(headerTitleDrawable); headerTitle.setImageDrawable(headerTitleDrawable);
final Resources r = header.getResources(); final Resources r = header.getResources();
final int newHeight = header.getHeight() + 2 * r.getDimensionPixelSize(R.dimen.cpp_onscreen_main_padding); final int newHeight = header.getHeight() + 2 * r
.getDimensionPixelSize(R.dimen.cpp_onscreen_main_padding);
content.setVisibility(View.GONE); content.setVisibility(View.GONE);
setHeight(newHeight); setHeight(newHeight);
folded = true; folded = true;
@ -283,7 +440,7 @@ public class FloatingCalculatorView {
if (folded) { if (folded) {
headerTitle.setImageDrawable(null); headerTitle.setImageDrawable(null);
content.setVisibility(View.VISIBLE); content.setVisibility(View.VISIBLE);
setHeight(state.getHeight()); setHeight(state.height);
folded = false; folded = false;
} }
} }
@ -300,14 +457,9 @@ public class FloatingCalculatorView {
public void minimize() { public void minimize() {
checkInit(); checkInit();
if (!minimized) { if (!minimized) {
persistState(context, getCurrentState(!folded)); saveState();
detach(); detach();
listener.onViewMinimized();
if (viewListener != null) {
viewListener.onViewMinimized();
}
minimized = true; minimized = true;
} }
} }
@ -317,150 +469,31 @@ public class FloatingCalculatorView {
if (!shown) { if (!shown) {
return; return;
} }
saveState();
persistState(context, getCurrentState(!folded));
detach(); detach();
listener.onViewHidden();
if (viewListener != null) {
viewListener.onViewHidden();
}
shown = false; shown = false;
} }
private void saveState() {
final SharedPreferences.Editor editor = myPreferences.edit();
getState().save(editor);
editor.apply();
}
@Nonnull @Nonnull
private WindowManager getWindowManager() { private WindowManager getWindowManager() {
return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)); return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
} }
@Nonnull @Nonnull
public FloatingCalculatorViewState getCurrentState(boolean useRealSize) { public State getState() {
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams(); final WindowManager.LayoutParams params =
if (useRealSize) { (WindowManager.LayoutParams) root.getLayoutParams();
return FloatingCalculatorViewState if (!folded) {
.create(params.width, params.height, params.x, params.y); return new State(params.width, params.height, params.x, params.y);
} else { } else {
return FloatingCalculatorViewState return new State(state.width, state.height, params.x, params.y);
.create(state.getWidth(), state.getHeight(), params.x, params.y);
}
}
private static class WindowDragTouchListener implements View.OnTouchListener {
private static final float DIST_EPS = 0f;
private static final float DIST_MAX = 100000f;
private static final long TIME_EPS = 0L;
@Nonnull
private final WindowManager wm;
@Nonnull
private final View view;
private int orientation;
private float x0;
private float y0;
private long time = 0;
private int displayWidth;
private int displayHeight;
public WindowDragTouchListener(@Nonnull WindowManager wm,
@Nonnull View view) {
this.wm = wm;
this.view = view;
initDisplayParams();
}
@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(Locale.ENGLISH, "%.2f", value);
}
}
@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;
} }
} }
} }

View File

@ -1,181 +0,0 @@
/*
* 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.floating;
import android.content.SharedPreferences;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.solovyev.android.prefs.AbstractPreference;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class FloatingCalculatorViewState implements Parcelable {
private static final String TAG = FloatingCalculatorViewState.class.getSimpleName();
private int width;
private int height;
private int x;
private int y;
public static final Parcelable.Creator<FloatingCalculatorViewState> CREATOR = new Parcelable.Creator<FloatingCalculatorViewState>() {
public FloatingCalculatorViewState createFromParcel(@Nonnull Parcel in) {
return FloatingCalculatorViewState.fromParcel(in);
}
public FloatingCalculatorViewState[] newArray(int size) {
return new FloatingCalculatorViewState[size];
}
};
private FloatingCalculatorViewState() {
}
@Nonnull
private static FloatingCalculatorViewState fromParcel(@Nonnull Parcel in) {
final FloatingCalculatorViewState result = new FloatingCalculatorViewState();
result.width = in.readInt();
result.height = in.readInt();
result.x = in.readInt();
result.y = in.readInt();
return result;
}
@Nonnull
public static FloatingCalculatorViewState createDefault() {
return create(200, 400, 0, 0);
}
@Nonnull
public static FloatingCalculatorViewState create(int width, int height, int x, int y) {
final FloatingCalculatorViewState result = new FloatingCalculatorViewState();
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<FloatingCalculatorViewState> {
public Preference(@Nonnull String key, @Nullable FloatingCalculatorViewState defaultValue) {
super(key, defaultValue);
}
@Nullable
@Override
protected FloatingCalculatorViewState getPersistedValue(@Nonnull SharedPreferences preferences) {
try {
final FloatingCalculatorViewState result = new FloatingCalculatorViewState();
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
FloatingCalculatorViewState 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);
}
}
}