new plotter

This commit is contained in:
Sergey Solovyev 2013-01-14 17:52:22 +04:00
parent 8fb1272bd2
commit f08e03761b
15 changed files with 187 additions and 54 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -298,6 +298,8 @@
<string name="cpp_dash_dotted_line_style">Dash dotted (-.-.-)</string> <string name="cpp_dash_dotted_line_style">Dash dotted (-.-.-)</string>
<string name="cpp_plotter">Function plotter</string> <string name="cpp_plotter">Function plotter</string>
<string name="cpp_plot_screenshot">Capture screenshot</string>
<string name="cpp_plot_screenshot_saved">Screenshot successfully saved: %1$s!</string>
<string name="cpp_plot_unable_to_save_screenshot">Screenshot cannot be saved as sdcard is not mounted. Mount sdcard and try again!</string>
</resources> </resources>

View File

@ -6,7 +6,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Environment;
import android.util.Log; import android.util.Log;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -67,20 +66,16 @@ public final class AndroidUtils2 {
public static String saveBitmap(@NotNull Bitmap bitmap, public static String saveBitmap(@NotNull Bitmap bitmap,
@NotNull String path, @NotNull String path,
@NotNull String fileName) { @NotNull String fileName) {
final File sdcardPath = Environment.getExternalStorageDirectory(); final File filePath = new File(path);
final File filePath = new File(sdcardPath, path);
filePath.mkdirs(); filePath.mkdirs();
final String fullFileName = fileName + "_" + System.currentTimeMillis() + ".png"; final File file = new File(path, fileName);
final File file = new File(path, fullFileName);
if (!file.exists()) { if (!file.exists()) {
final String name = file.getAbsolutePath(); final String name = file.getAbsolutePath();
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
fos = new FileOutputStream(name); fos = new FileOutputStream(name);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush(); fos.flush();
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {

View File

@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.android.vending.BILLING"/> <uses-permission android:name="com.android.vending.BILLING"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--TODO: REMOVE IN PRODUCTION--> <!--TODO: REMOVE IN PRODUCTION-->
<!--<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>--> <!--<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>-->

View File

@ -23,6 +23,11 @@
a:icon="@drawable/ab_list" a:icon="@drawable/ab_list"
a:showAsAction="always"/> a:showAsAction="always"/>
<item a:id="@+id/menu_plot_schreeshot"
a:title="@string/cpp_plot_screenshot"
a:icon="@drawable/ab_camera"
a:showAsAction="always"/>
<item a:id="@+id/menu_plot_settings" <item a:id="@+id/menu_plot_settings"
a:title="@string/c_settings" a:title="@string/c_settings"
a:icon="@drawable/ab_settings" a:icon="@drawable/ab_settings"

View File

@ -2,8 +2,10 @@ package org.solovyev.android.calculator.plot;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Paint; import android.graphics.Paint;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.view.View; import android.view.View;
@ -13,17 +15,30 @@ import com.actionbarsherlock.view.MenuItem;
import org.achartengine.renderer.XYMultipleSeriesRenderer; import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*; import org.solovyev.android.AndroidUtils2;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.CalculatorEventData;
import org.solovyev.android.calculator.CalculatorEventHolder;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.CalculatorFragment;
import org.solovyev.android.calculator.CalculatorUtils;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.menu.AMenuItem; import org.solovyev.android.menu.AMenuItem;
import org.solovyev.android.menu.ActivityMenu; import org.solovyev.android.menu.ActivityMenu;
import org.solovyev.android.menu.IdentifiableMenuItem; import org.solovyev.android.menu.IdentifiableMenuItem;
import org.solovyev.android.menu.ListActivityMenu; import org.solovyev.android.menu.ListActivityMenu;
import org.solovyev.android.sherlock.menu.SherlockMenuHelper; import org.solovyev.android.sherlock.menu.SherlockMenuHelper;
import org.solovyev.common.JPredicate; import org.solovyev.common.JPredicate;
import org.solovyev.common.msg.MessageType;
import java.io.File;
import java.io.Serializable; import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -68,7 +83,11 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
@NotNull @NotNull
private PlotData plotData = new PlotData(Collections.<PlotFunction>emptyList(), false); private PlotData plotData = new PlotData(Collections.<PlotFunction>emptyList(), false);
@NotNull @NotNull
private PlotBoundaries initialPlotBoundaries;
@NotNull
private ActivityMenu<Menu, MenuItem> fragmentMenu; private ActivityMenu<Menu, MenuItem> fragmentMenu;
// thread which calculated data for graph view // thread which calculated data for graph view
@ -85,8 +104,8 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle in) {
super.onCreate(savedInstanceState); super.onCreate(in);
// todo serso: init variable properly // todo serso: init variable properly
boolean paneFragment = true; boolean paneFragment = true;
@ -96,7 +115,20 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
this.bgColor = getResources().getColor(android.R.color.transparent); this.bgColor = getResources().getColor(android.R.color.transparent);
} }
setHasOptionsMenu(true); final PlotBoundaries savedPlotBoundaries;
if (in != null) {
savedPlotBoundaries = (PlotBoundaries) in.getSerializable(PLOT_BOUNDARIES);
} else {
savedPlotBoundaries = null;
}
if (savedPlotBoundaries != null) {
initialPlotBoundaries = savedPlotBoundaries;
} else {
initialPlotBoundaries = PlotBoundaries.newDefaultInstance();
}
setHasOptionsMenu(true);
} }
@Override @Override
@ -117,8 +149,8 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
super.onResume(); super.onResume();
plotData = Locator.getInstance().getPlotter().getPlotData(); plotData = Locator.getInstance().getPlotter().getPlotData();
createChart(plotData); createChart(plotData, initialPlotBoundaries);
createGraphicalView(getView(), plotData); createGraphicalView(getView(), plotData, initialPlotBoundaries);
getSherlockActivity().invalidateOptionsMenu(); getSherlockActivity().invalidateOptionsMenu();
} }
@ -140,11 +172,11 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
public void run() { public void run() {
getSherlockActivity().invalidateOptionsMenu(); getSherlockActivity().invalidateOptionsMenu();
createChart(plotData); createChart(plotData, initialPlotBoundaries);
final View view = getView(); final View view = getView();
if (view != null) { if (view != null) {
createGraphicalView(view, plotData); createGraphicalView(view, plotData, initialPlotBoundaries);
} }
} }
}); });
@ -152,16 +184,16 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
protected abstract void onError(); protected abstract void onError();
protected abstract void createGraphicalView(@NotNull View view, @NotNull PlotData plotData); protected abstract void createGraphicalView(@NotNull View view, @NotNull PlotData plotData, @NotNull PlotBoundaries plotBoundaries);
protected abstract void createChart(@NotNull PlotData plotData); protected abstract void createChart(@NotNull PlotData plotData, @NotNull PlotBoundaries plotBoundaries);
protected double getMaxValue(@Nullable PlotBoundaries plotBoundaries) { protected double getMaxXValue(@Nullable PlotBoundaries plotBoundaries) {
return plotBoundaries == null ? DEFAULT_MAX_NUMBER : plotBoundaries.getXMax(); return plotBoundaries == null ? DEFAULT_MAX_NUMBER : plotBoundaries.getXMax();
} }
protected double getMinValue(@Nullable PlotBoundaries plotBoundaries) { protected double getMinXValue(@Nullable PlotBoundaries plotBoundaries) {
return plotBoundaries == null ? DEFAULT_MIN_NUMBER : plotBoundaries.getXMin(); return plotBoundaries == null ? DEFAULT_MIN_NUMBER : plotBoundaries.getXMin();
} }
@ -227,13 +259,28 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
@Override @Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) { public void onClick(@NotNull MenuItem data, @NotNull Context context) {
Locator.getInstance().getPlotter().setPlot3d(false); Locator.getInstance().getPlotter().setPlot3d(false);
} }
}; };
menuItems.add(plot2dMenuItem); menuItems.add(plot2dMenuItem);
final IdentifiableMenuItem<MenuItem> captureScreenshotMenuItem = new IdentifiableMenuItem<MenuItem>() {
@NotNull
@Override
public Integer getItemId() {
return R.id.menu_plot_schreeshot;
}
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
captureScreehshot();
}
};
menuItems.add(captureScreenshotMenuItem);
final boolean plot3dVisible = !plotData.isPlot3d() && is3dPlotSupported(); final boolean plot3dVisible = !plotData.isPlot3d() && is3dPlotSupported();
final boolean plot2dVisible = plotData.isPlot3d() && Locator.getInstance().getPlotter().is2dPlotPossible(); final boolean plot2dVisible = plotData.isPlot3d() && Locator.getInstance().getPlotter().is2dPlotPossible();
final boolean captureScreenshotVisible = isScreenshotSupported();
fragmentMenu = ListActivityMenu.fromResource(R.menu.plot_menu, menuItems, SherlockMenuHelper.getInstance(), new JPredicate<AMenuItem<MenuItem>>() { fragmentMenu = ListActivityMenu.fromResource(R.menu.plot_menu, menuItems, SherlockMenuHelper.getInstance(), new JPredicate<AMenuItem<MenuItem>>() {
@Override @Override
public boolean apply(@Nullable AMenuItem<MenuItem> menuItem) { public boolean apply(@Nullable AMenuItem<MenuItem> menuItem) {
@ -241,7 +288,9 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
return !plot3dVisible; return !plot3dVisible;
} else if ( menuItem == plot2dMenuItem ) { } else if ( menuItem == plot2dMenuItem ) {
return !plot2dVisible; return !plot2dVisible;
} } else if ( menuItem == captureScreenshotMenuItem ) {
return !captureScreenshotVisible;
}
return false; return false;
} }
}); });
@ -252,7 +301,34 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
} }
} }
protected abstract boolean is3dPlotSupported(); protected abstract boolean isScreenshotSupported();
@NotNull
protected abstract Bitmap getScreehshot();
private void captureScreehshot() {
if ( isScreenshotSupported() ) {
final Bitmap screenshot = getScreehshot();
final String screenShotFileName = generateScreenshotFileName();
final File externalFilesDir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
if (externalFilesDir != null) {
final String path = externalFilesDir.getPath();
AndroidUtils2.saveBitmap(screenshot, path, screenShotFileName);
Locator.getInstance().getNotifier().showMessage(R.string.cpp_plot_screenshot_saved, MessageType.info, path + "/" + screenShotFileName);
} else {
Locator.getInstance().getNotifier().showMessage(R.string.cpp_plot_unable_to_save_screenshot, MessageType.error);
}
}
}
private String generateScreenshotFileName() {
final Date now = new Date();
final String timestamp = new SimpleDateFormat("yyyy.MM.dd HH.mm.ss.S").format(now);
return "cpp-screenshot-" + timestamp + ".png";
}
protected abstract boolean is3dPlotSupported();
@Override @Override
public void onPrepareOptionsMenu(Menu menu) { public void onPrepareOptionsMenu(Menu menu) {

View File

@ -1,5 +1,6 @@
package org.solovyev.android.calculator.plot; package org.solovyev.android.calculator.plot;
import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -35,7 +36,7 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
} }
@Override @Override
protected void createGraphicalView(@NotNull View root, @NotNull PlotData plotData) { protected void createGraphicalView(@NotNull View root, @NotNull PlotData plotData, @NotNull PlotBoundaries plotBoundaries) {
// remove old // remove old
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout); final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
@ -74,15 +75,28 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor())); graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor()));
graphView.setFunctionPlotDefs(arityFunctions); graphView.setFunctionPlotDefs(arityFunctions);
graphView.setXRange((float)plotBoundaries.getXMin(), (float)plotBoundaries.getXMax());
graphContainer.addView((View) graphView); graphContainer.addView((View) graphView);
} }
@Override @Override
protected void createChart(@NotNull PlotData plotData) { protected void createChart(@NotNull PlotData plotData, @NotNull PlotBoundaries plotBoundaries) {
} }
@Override @Override
protected boolean isScreenshotSupported() {
return true;
}
@NotNull
@Override
protected Bitmap getScreehshot() {
assert this.graphView != null;
return this.graphView.captureScreenshot();
}
@Override
protected boolean is3dPlotSupported() { protected boolean is3dPlotSupported() {
return true; return true;
} }

View File

@ -11,9 +11,13 @@ import android.util.Log;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils2;
import javax.microedition.khronos.egl.*; import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11; import javax.microedition.khronos.opengles.GL11;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -36,10 +40,11 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
abstract void onSurfaceCreated(GL10 gl, int width, int height); abstract void onSurfaceCreated(GL10 gl, int width, int height);
public String captureScreenshot() { @NotNull
Bitmap bitmap = getRawPixels(gl, width, height); public Bitmap captureScreenshot() {
bitmapBGRtoRGB(bitmap, width, height); final Bitmap result = getRawPixels(gl, width, height);
return AndroidUtils2.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator"); bitmapBGRtoRGB(result, width, height);
return result;
} }
private static Bitmap getRawPixels(GL10 gl, int width, int height) { private static Bitmap getRawPixels(GL10 gl, int width, int height) {

View File

@ -4,7 +4,12 @@ package org.solovyev.android.calculator.plot;
import android.content.Context; import android.content.Context;
import android.graphics.*; import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@ -12,7 +17,6 @@ import android.widget.Scroller;
import android.widget.ZoomButtonsController; import android.widget.ZoomButtonsController;
import org.javia.arity.Function; import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils2;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -67,14 +71,22 @@ public class Graph2dView extends View implements GraphView {
textPaint.setAntiAlias(true); textPaint.setAntiAlias(true);
} }
public String captureScreenshot() { @NotNull
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); public Bitmap captureScreenshot() {
Canvas canvas = new Canvas(bitmap); final Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(result);
onDraw(canvas); onDraw(canvas);
return AndroidUtils2.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator"); return result;
} }
private void clearAllGraphs() { @Override
public void setXRange(float xMin, float xMax) {
this.gwidth = xMax - xMin;
this.currentX = xMax - this.gwidth / 2;
}
private void clearAllGraphs() {
for (GraphData graph : graphs) { for (GraphData graph : graphs) {
graph.clear(); graph.clear();
} }
@ -517,11 +529,11 @@ public class Graph2dView extends View implements GraphView {
} }
private boolean canZoomIn() { private boolean canZoomIn() {
return gwidth > 1f; return true;
} }
private boolean canZoomOut() { private boolean canZoomOut() {
return gwidth < 50; return true;
} }
private void invalidateGraphs() { private void invalidateGraphs() {

View File

@ -4,7 +4,12 @@ package org.solovyev.android.calculator.plot;
import android.content.Context; import android.content.Context;
import android.graphics.*; import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@ -12,7 +17,6 @@ import android.widget.Scroller;
import android.widget.ZoomButtonsController; import android.widget.ZoomButtonsController;
import org.javia.arity.Function; import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils2;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -94,14 +98,21 @@ public class Graph2dViewNew extends View implements GraphView {
height = this.getHeight(); height = this.getHeight();
} }
public String captureScreenshot() { @NotNull
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); public Bitmap captureScreenshot() {
Canvas canvas = new Canvas(bitmap); final Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(result);
onDraw(canvas); onDraw(canvas);
return AndroidUtils2.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator"); return result;
} }
private void clearAllGraphs() { @Override
public void setXRange(float xMin, float xMax) {
this.x0 = xMin + graphWidth / 2;
this.graphWidth = xMax - xMin;
}
private void clearAllGraphs() {
for (GraphData graph : graphs) { for (GraphData graph : graphs) {
graph.clear(); graph.clear();
} }

View File

@ -101,11 +101,11 @@ public class Graph3dView extends GLView implements GraphView {
} }
private boolean canZoomIn(float zoom) { private boolean canZoomIn(float zoom) {
return zoom > .2f; return true;
} }
private boolean canZoomOut(float zoom) { private boolean canZoomOut(float zoom) {
return zoom < 5; return true;
} }
@Override @Override
@ -190,7 +190,12 @@ public class Graph3dView extends GLView implements GraphView {
isDirty = true; isDirty = true;
} }
@Override @Override
public void setXRange(float xMin, float xMax) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void onSurfaceCreated(GL10 gl, int width, int height) { public void onSurfaceCreated(GL10 gl, int width, int height) {
gl.glDisable(GL10.GL_DITHER); gl.glDisable(GL10.GL_DITHER);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

View File

@ -2,6 +2,7 @@
package org.solovyev.android.calculator.plot; package org.solovyev.android.calculator.plot;
import android.graphics.Bitmap;
import android.widget.ZoomButtonsController; import android.widget.ZoomButtonsController;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -18,7 +19,13 @@ public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHa
public void onPause(); public void onPause();
public void onResume(); public void onResume();
public String captureScreenshot(); @NotNull
public Bitmap captureScreenshot();
void setXRange(float xMin, float xMax);
/* void increaseDensity();
void decreaseDensity();*/
/* /*
********************************************************************** **********************************************************************