1 // This file is part of Golly.
2 // See docs/License.html for the copyright notice.
3 
4 package net.sf.golly;
5 
6 import java.io.File;
7 
8 import android.app.Activity;
9 import android.app.AlertDialog;
10 import android.content.ClipData;
11 import android.content.ClipboardManager;
12 import android.content.Context;
13 import android.content.DialogInterface;
14 import android.content.Intent;
15 import android.graphics.Color;
16 import android.graphics.Typeface;
17 import android.media.AudioManager;
18 import android.media.ToneGenerator;
19 import android.os.Bundle;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.MessageQueue;
23 import android.text.InputType;
24 import android.util.DisplayMetrics;
25 import android.util.Log;
26 import android.view.Menu;
27 import android.view.MenuInflater;
28 import android.view.MenuItem;
29 import android.view.View;
30 import android.widget.Button;
31 import android.widget.EditText;
32 import android.widget.LinearLayout;
33 import android.widget.PopupMenu;
34 import android.widget.ProgressBar;
35 import android.widget.RelativeLayout;
36 import android.widget.TextView;
37 
38 public class MainActivity extends Activity {
39 
40     // see jnicalls.cpp for these native routines:
nativeClassInit()41     private static native void nativeClassInit();   // this MUST be static
nativeCreate()42     private native void nativeCreate();             // the rest must NOT be static
nativeDestroy()43     private native void nativeDestroy();
nativeStartGenerating()44     private native void nativeStartGenerating();
nativeStopGenerating()45     private native void nativeStopGenerating();
nativeStopBeforeNew()46     private native void nativeStopBeforeNew();
nativePauseGenerating()47     private native void nativePauseGenerating();
nativeResumeGenerating()48     private native void nativeResumeGenerating();
nativeResetPattern()49     private native void nativeResetPattern();
nativeUndo()50     private native void nativeUndo();
nativeRedo()51     private native void nativeRedo();
nativeCanReset()52     private native boolean nativeCanReset();
nativeAllowUndo()53     private native boolean nativeAllowUndo();
nativeCanUndo()54     private native boolean nativeCanUndo();
nativeCanRedo()55     private native boolean nativeCanRedo();
nativeInfoAvailable()56     private native boolean nativeInfoAvailable();
nativeIsGenerating()57     private native boolean nativeIsGenerating();
nativeGetStatusColor()58     private native int nativeGetStatusColor();
nativeGetStatusLine(int line)59     private native String nativeGetStatusLine(int line);
nativeGetPasteMode()60     private native String nativeGetPasteMode();
nativeGetRandomFill()61     private native String nativeGetRandomFill();
nativeNewPattern()62     private native void nativeNewPattern();
nativeFitPattern()63     private native void nativeFitPattern();
nativeGenerate()64     private native void nativeGenerate();
nativeStep()65     private native void nativeStep();
nativeStep1()66     private native void nativeStep1();
nativeFaster()67     private native void nativeFaster();
nativeSlower()68     private native void nativeSlower();
nativeScale1to1()69     private native void nativeScale1to1();
nativeBigger()70     private native void nativeBigger();
nativeSmaller()71     private native void nativeSmaller();
nativeMiddle()72     private native void nativeMiddle();
nativeSetMode(int mode)73     private native void nativeSetMode(int mode);
nativeGetMode()74     private native int nativeGetMode();
nativeCalculateSpeed()75     private native int nativeCalculateSpeed();
nativeNumLayers()76     private native int nativeNumLayers();
nativePasteExists()77     private native boolean nativePasteExists();
nativeSelectionExists()78     private native boolean nativeSelectionExists();
nativePaste()79     private native void nativePaste();
nativeSelectAll()80     private native void nativeSelectAll();
nativeRemoveSelection()81     private native void nativeRemoveSelection();
nativeCutSelection()82     private native void nativeCutSelection();
nativeCopySelection()83     private native void nativeCopySelection();
nativeClearSelection(int inside)84     private native void nativeClearSelection(int inside);
nativeShrinkSelection()85     private native void nativeShrinkSelection();
nativeFitSelection()86     private native void nativeFitSelection();
nativeRandomFill()87     private native void nativeRandomFill();
nativeFlipSelection(int y)88     private native void nativeFlipSelection(int y);
nativeRotateSelection(int clockwise)89     private native void nativeRotateSelection(int clockwise);
nativeAdvanceSelection(int inside)90     private native void nativeAdvanceSelection(int inside);
nativeAbortPaste()91     private native void nativeAbortPaste();
nativeDoPaste(int toselection)92     private native void nativeDoPaste(int toselection);
nativeFlipPaste(int y)93     private native void nativeFlipPaste(int y);
nativeRotatePaste(int clockwise)94     private native void nativeRotatePaste(int clockwise);
nativeClearMessage()95     private native void nativeClearMessage();
nativeGetValidExtensions()96     private native String nativeGetValidExtensions();
nativeValidExtension(String filename)97     private native boolean nativeValidExtension(String filename);
nativeFileExists(String filename)98     private native boolean nativeFileExists(String filename);
nativeSavePattern(String filename)99     private native void nativeSavePattern(String filename);
nativeOpenFile(String filepath)100     private native void nativeOpenFile(String filepath);
nativeSetFullScreen(boolean fullscreen)101     private native void nativeSetFullScreen(boolean fullscreen);
nativeChangeRule(String rule)102     private native void nativeChangeRule(String rule);
nativeLexiconPattern(String pattern)103     private native void nativeLexiconPattern(String pattern);
nativeDrawingState()104     private native int nativeDrawingState();
105 
106     // local fields:
107     private static boolean fullscreen = false;      // in full screen mode?
108     private boolean widescreen;                     // use different layout for wide screens?
109     private Button ssbutton;                        // Start/Stop button
110     private Button resetbutton;                     // Reset button (used if wide screen)
111     private Button undobutton;                      // Undo button
112     private Button redobutton;                      // Redo button
113     private Button editbutton;                      // Edit/Paste button
114     private Button statebutton;                     // button to change drawing state
115     private Button modebutton;                      // Draw/Pick/Select/Move button
116     private Button drawbutton;                      // Draw button (used if wide screen)
117     private Button pickbutton;                      // Pick button (used if wide screen)
118     private Button selectbutton;                    // Select button (used if wide screen)
119     private Button movebutton;                      // Move button (used if wide screen)
120     private Button infobutton;                      // Info button
121     private Button restorebutton;                   // Restore button
122     private TextView status1, status2, status3;     // status bar has 3 lines
123     private int statuscolor = 0xFF000000;           // background color of status bar
124     private PatternGLSurfaceView pattView;          // OpenGL ES is used to draw patterns
125     private Handler genhandler;                     // for generating patterns
126     private Runnable generate;                      // code started/stopped by genhandler
127     private int geninterval;                        // interval between nativeGenerate calls (in millisecs)
128     private Handler callhandler;                    // for calling a method again
129     private Runnable callagain;                     // code that calls method
130     private String methodname;                      // name of method to call
131     private View currview;                          // current view parameter
132     private MenuItem curritem;                      // current menu item parameter
133     private PopupMenu popup;                        // used for all pop-up menus
134     private boolean stopped = true;                 // generating is stopped?
135 
136     // -----------------------------------------------------------------------------
137 
138     // this stuff is used to display a progress bar:
139 
140     private static boolean cancelProgress = false;  // cancel progress dialog?
141     private static long progstart, prognext;        // for progress timing
142     private static int progresscount = 0;           // if > 0 then BeginProgress has been called
143     private TextView progtitle;                     // title above progress bar
144     private ProgressBar progbar;                    // progress bar
145     private LinearLayout proglayout;                // view containing progress bar
146 
147     // -----------------------------------------------------------------------------
148 
149     // this stuff is used in other activities:
150 
151     public final static String OPENFILE_MESSAGE = "net.sf.golly.OPENFILE";
152     public final static String RULE_MESSAGE = "net.sf.golly.RULE";
153     public final static String LEXICON_MESSAGE = "net.sf.golly.LEXICON";
154 
155     // -----------------------------------------------------------------------------
156 
157     // the following stuff allows time consuming code (like nativeGenerate) to periodically
158     // check if any user events need to be processed, but without blocking the UI thread
159     // (thanks to http://stackoverflow.com/questions/4994263/how-can-i-do-non-blocking-events-processing-on-android)
160 
161     private boolean processingevents = false;
162     private Handler evthandler = null;
163 
164     private Runnable doevents = new Runnable() {
165         public void run() {
166             Looper looper = Looper.myLooper();
167             looper.quit();
168             evthandler.removeCallbacks(this);
169             evthandler = null;
170         }
171     };
172 
173     private class IdleHandler implements MessageQueue.IdleHandler {
174         private Looper idlelooper;
IdleHandler(Looper looper)175         protected IdleHandler(Looper looper) {
176             idlelooper = looper;
177         }
queueIdle()178         public boolean queueIdle() {
179             evthandler = new Handler(idlelooper);
180             evthandler.post(doevents);
181             return(false);
182         }
183     };
184 
185     // this method is called from C++ code (see jnicalls.cpp)
CheckMessageQueue()186     private void CheckMessageQueue() {
187         // process any pending UI events in message queue
188         if (!processingevents) {
189             Looper looper = Looper.myLooper();
190             looper.myQueue().addIdleHandler(new IdleHandler(looper));
191             processingevents = true;
192             try {
193                 looper.loop();
194             } catch (RuntimeException re) {
195                 // looper.quit() in doevents causes an exception
196             }
197             processingevents = false;
198         }
199     }
200 
201     // -----------------------------------------------------------------------------
202 
203     static {
nativeClassInit()204         nativeClassInit();      // caches Java method IDs
205     }
206 
207     // -----------------------------------------------------------------------------
208 
209     @Override
onCreate(Bundle savedInstanceState)210     protected void onCreate(Bundle savedInstanceState) {
211         super.onCreate(savedInstanceState);
212 
213         // Log.i("Golly","screen density in dpi = " + Integer.toString(metrics.densityDpi));
214         // eg. densityDpi = 320 on Nexus 7
215 
216         DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
217         float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
218 
219         // Log.i("Golly","screen width in dp = " + Integer.toString(config.screenWidthDp));
220         // eg. on Nexus 7 screenWidthDp = 600 in portrait, 960 in landscape
221         widescreen = dpWidth >= 600;
222 
223         if (widescreen) {
224             // use a layout that has more buttons
225             setContentView(R.layout.main_layout_wide);
226             resetbutton = (Button) findViewById(R.id.reset);
227             drawbutton = (Button) findViewById(R.id.drawmode);
228             pickbutton = (Button) findViewById(R.id.pickmode);
229             selectbutton = (Button) findViewById(R.id.selectmode);
230             movebutton = (Button) findViewById(R.id.movemode);
231             if (dpWidth >= 800) {
232                 // show nicer text in some buttons
233                 ((Button) findViewById(R.id.slower)).setText("Slower");
234                 ((Button) findViewById(R.id.faster)).setText("Faster");
235                 ((Button) findViewById(R.id.smaller)).setText("Smaller");
236                 ((Button) findViewById(R.id.scale1to1)).setText("Scale=1:1");
237                 ((Button) findViewById(R.id.bigger)).setText("Bigger");
238                 ((Button) findViewById(R.id.middle)).setText("Middle");
239             }
240         } else {
241             // screen isn't wide enough to have all the buttons we'd like
242             setContentView(R.layout.main_layout);
243             modebutton = (Button) findViewById(R.id.touchmode);
244         }
245 
246         // following stuff is used in both layouts
247         ssbutton = (Button) findViewById(R.id.startstop);
248         undobutton = (Button) findViewById(R.id.undo);
249         redobutton = (Button) findViewById(R.id.redo);
250         editbutton = (Button) findViewById(R.id.edit);
251         statebutton = (Button) findViewById(R.id.state);
252         infobutton = (Button) findViewById(R.id.info);
253         restorebutton = (Button) findViewById(R.id.restore);
254         status1 = (TextView) findViewById(R.id.status1);
255         status2 = (TextView) findViewById(R.id.status2);
256         status3 = (TextView) findViewById(R.id.status3);
257         progbar = (ProgressBar) findViewById(R.id.progress_bar);
258         proglayout = (LinearLayout) findViewById(R.id.progress_layout);
259         progtitle = (TextView) findViewById(R.id.progress_title);
260 
261         nativeCreate();         // must be called every time (to cache this instance)
262 
263         // this will call the PatternGLSurfaceView constructor
264         pattView = (PatternGLSurfaceView) findViewById(R.id.patternview);
265 
266         restorebutton.setVisibility(View.INVISIBLE);
267         proglayout.setVisibility(LinearLayout.INVISIBLE);
268 
269         // create handler and runnable for generating patterns
270         geninterval = nativeCalculateSpeed();
271         genhandler = new Handler();
272         generate = new Runnable() {
273             public void run() {
274                 if (!stopped) {
275                     nativeGenerate();
276                     genhandler.postDelayed(this, geninterval);
277                     // nativeGenerate will be called again after given interval
278                 }
279             }
280         };
281 
282         // create handler and runnable for calling a method again when the user
283         // invokes certain events while the next generation is being calculated
284         callhandler = new Handler();
285         callagain = new Runnable() {
286             public void run() {
287                 if (methodname.equals("doStartStop")) doStartStop(currview); else
288                 if (methodname.equals("doStep"))      doStep(currview); else
289                 if (methodname.equals("doNew"))       doNewPattern(currview); else
290                 if (methodname.equals("doUndo"))      doUndo(currview); else
291                 if (methodname.equals("doRedo"))      doRedo(currview); else
292                 if (methodname.equals("doRule"))      doRule(currview); else
293                 if (methodname.equals("doInfo"))      doInfo(currview); else
294                 if (methodname.equals("doSave"))      doSave(currview); else
295                 if (methodname.equals("doReset"))     doReset(currview); else
296                 if (methodname.equals("doPaste"))     doPaste(currview); else
297                 if (methodname.equals("doSelItem"))   doSelectionItem(curritem); else
298                 // string mismatch
299                 Log.e("Fix callagain", methodname);
300             }
301         };
302     }
303 
304     // -----------------------------------------------------------------------------
305 
306     @Override
onNewIntent(Intent intent)307     protected void onNewIntent(Intent intent) {
308     	// check for messages sent by other activities
309     	String filepath = intent.getStringExtra(OPENFILE_MESSAGE);
310     	if (filepath != null) {
311     	    nativeOpenFile(filepath);
312     	}
313     	String rule = intent.getStringExtra(RULE_MESSAGE);
314     	if (rule != null) {
315     	    nativeChangeRule(rule);
316     	}
317     	String pattern = intent.getStringExtra(LEXICON_MESSAGE);
318     	if (pattern != null) {
319     	    nativeLexiconPattern(pattern);
320     	}
321     }
322 
323     // -----------------------------------------------------------------------------
324 
325     @Override
onCreateOptionsMenu(Menu menu)326     public boolean onCreateOptionsMenu(Menu menu) {
327         // add main.xml items to the action bar
328         getMenuInflater().inflate(R.menu.main, menu);
329         return true;
330     }
331 
332     // -----------------------------------------------------------------------------
333 
334     @Override
onOptionsItemSelected(MenuItem item)335     public boolean onOptionsItemSelected(MenuItem item) {
336         // action bar item has been tapped
337         nativeClearMessage();
338         Intent intent;
339         switch (item.getItemId()) {
340             case R.id.open:
341                 intent = new Intent(this, OpenActivity.class);
342                 startActivity(intent);
343                 return true;
344             case R.id.settings:
345                 intent = new Intent(this, SettingsActivity.class);
346                 startActivity(intent);
347                 return true;
348             case R.id.help:
349                 intent = new Intent(this, HelpActivity.class);
350                 startActivity(intent);
351                 return true;
352         }
353         return super.onOptionsItemSelected(item);
354     }
355 
356     // -----------------------------------------------------------------------------
357 
358     @Override
onPause()359     protected void onPause() {
360         super.onPause();
361         pattView.onPause();
362         stopped = true;
363     }
364 
365     // -----------------------------------------------------------------------------
366 
367     @Override
onResume()368     protected void onResume() {
369         super.onResume();
370         pattView.onResume();
371 
372         if (fullscreen) {
373             fullscreen = false;         // following will set it true
374             toggleFullScreen(null);
375         } else {
376             updateButtons();
377             UpdateEditBar();
378         }
379 
380         if (nativeIsGenerating()) {
381             stopped = false;
382             genhandler.post(generate);
383         }
384     }
385 
386     // -----------------------------------------------------------------------------
387 
388     @Override
onDestroy()389     protected void onDestroy() {
390         stopped = true;             // should have been done in OnPause, but play safe
391         nativeDestroy();
392         super.onDestroy();
393     }
394 
395     // -----------------------------------------------------------------------------
396 
deleteTempFiles()397     private void deleteTempFiles() {
398         File dir = getCacheDir();
399         File[] files = dir.listFiles();
400         if (files != null) {
401             for (File file : files) {
402                 if (file.getName().startsWith("GET-") || file.getName().endsWith(".html")) {
403                     // don't delete files created via "get:" links in HelpActivity
404                 } else {
405                     file.delete();
406                 }
407             }
408         }
409     }
410 
411     // -----------------------------------------------------------------------------
412 
updateButtons()413     private void updateButtons() {
414         if (fullscreen) return;
415 
416         if (nativeIsGenerating()) {
417             ssbutton.setText("Stop");
418             ssbutton.setTextColor(Color.rgb(255,0,0));
419             if (widescreen) resetbutton.setEnabled(true);
420             undobutton.setEnabled(nativeAllowUndo());
421             redobutton.setEnabled(false);
422         } else {
423             ssbutton.setText("Start");
424             ssbutton.setTextColor(Color.rgb(0,255,0));
425             if (widescreen) resetbutton.setEnabled(nativeCanReset());
426             undobutton.setEnabled(nativeAllowUndo() && (nativeCanReset() || nativeCanUndo()));
427             redobutton.setEnabled(nativeCanRedo());
428         }
429         infobutton.setEnabled(nativeInfoAvailable());
430     }
431 
432     // -----------------------------------------------------------------------------
433 
updateGeneratingSpeed()434     private void updateGeneratingSpeed() {
435         geninterval = nativeCalculateSpeed();
436         genhandler.removeCallbacks(generate);
437         stopped = false;
438         if (nativeIsGenerating()) genhandler.post(generate);
439     }
440 
441     // -----------------------------------------------------------------------------
442 
callAgainAfterDelay(String callname, View view, MenuItem item)443     private boolean callAgainAfterDelay(String callname, View view, MenuItem item) {
444         if (processingevents) {
445             // CheckMessageQueue has been called inside a (possibly) lengthy task
446             // so call the given method again after a short delay
447             methodname = callname;
448             if (view != null) currview = view;
449             if (item != null) curritem = item;
450             callhandler.postDelayed(callagain, 5);    // call after 5 millisecs
451             return true;
452         } else {
453             return false;
454         }
455     }
456 
457     // -----------------------------------------------------------------------------
458 
459     // called when the Start/Stop button is tapped
doStartStop(View view)460     public void doStartStop(View view) {
461         nativeClearMessage();
462         if (callAgainAfterDelay("doStartStop", view, null)) return;
463         if (nativeIsGenerating()) {
464             // stop calling nativeGenerate
465             stopped = true;
466             nativeStopGenerating();
467         } else {
468             nativeStartGenerating();
469             // nativeIsGenerating() might still be false (eg. if pattern is empty)
470             if (nativeIsGenerating()) {
471                 // start calling nativeGenerate
472                 geninterval = nativeCalculateSpeed();
473                 genhandler.removeCallbacks(generate);   // probably unnecessary here but play safe
474                 stopped = false;
475                 genhandler.post(generate);
476             }
477         }
478         updateButtons();
479     }
480 
481     // -----------------------------------------------------------------------------
482 
stopIfGenerating()483     private void stopIfGenerating() {
484         if (nativeIsGenerating()) {
485             // note that genhandler.removeCallbacks(generate) doesn't always work if
486             // processingevents is true, so we use a global flag to start/stop
487             // making calls to nativeGenerate
488             stopped = true;
489             nativeStopGenerating();
490         }
491     }
492 
493     // -----------------------------------------------------------------------------
494 
495     // called when the Step button is tapped
doStep(View view)496     public void doStep(View view) {
497         nativeClearMessage();
498         stopIfGenerating();
499         if (callAgainAfterDelay("doStep", view, null)) return;
500         nativeStep();
501         updateButtons();
502     }
503 
504     // -----------------------------------------------------------------------------
505 
506     // called when the Slower button is tapped
doSlower(View view)507     public void doSlower(View view) {
508         nativeClearMessage();
509         nativeSlower();
510         updateGeneratingSpeed();
511     }
512 
513     // -----------------------------------------------------------------------------
514 
515     // called when the Step=1 button is tapped
doStep1(View view)516     public void doStep1(View view) {
517         nativeClearMessage();
518         nativeStep1();
519         updateGeneratingSpeed();
520     }
521 
522     // -----------------------------------------------------------------------------
523 
524     // called when the Faster button is tapped
doFaster(View view)525     public void doFaster(View view) {
526         nativeClearMessage();
527         nativeFaster();
528         updateGeneratingSpeed();
529     }
530 
531     // -----------------------------------------------------------------------------
532 
533     // called when the Reset button is tapped
doReset(View view)534     public void doReset(View view) {
535         nativeClearMessage();
536         stopIfGenerating();
537         if (callAgainAfterDelay("doReset", view, null)) return;
538         nativeResetPattern();
539         updateButtons();
540     }
541 
542     // -----------------------------------------------------------------------------
543 
544     // called when the Control button is tapped
doControl(View view)545     public void doControl(View view) {
546         nativeClearMessage();
547         // display pop-up menu with these items: Step=1, Faster, Slower, Reset
548         popup = new PopupMenu(this, view);
549         MenuInflater inflater = popup.getMenuInflater();
550         inflater.inflate(R.menu.control_menu, popup.getMenu());
551         popup.show();
552     }
553 
554     // -----------------------------------------------------------------------------
555 
556     // called when item from control_menu is selected
doControlItem(MenuItem item)557     public void doControlItem(MenuItem item) {
558         if (item.getItemId() == R.id.reset) {
559             stopIfGenerating();
560             if (callAgainAfterDelay("doReset", null, item)) return;
561         }
562         popup.dismiss();
563         switch (item.getItemId()) {
564             case R.id.step1:  nativeStep1(); break;
565             case R.id.faster: nativeFaster(); break;
566             case R.id.slower: nativeSlower(); break;
567             case R.id.reset:  nativeResetPattern(); break;
568             default:          Log.e("Golly","Fix bug in doControlItem!");
569         }
570         if (item.getItemId() == R.id.reset) {
571             updateButtons();
572         } else {
573             updateGeneratingSpeed();
574         }
575     }
576 
577     // -----------------------------------------------------------------------------
578 
579     // called when the Fit button is tapped
doFitPattern(View view)580     public void doFitPattern(View view) {
581         nativeClearMessage();
582         nativeFitPattern();
583     }
584 
585     // -----------------------------------------------------------------------------
586 
587     // called when the Smaller button is tapped
doSmaller(View view)588     public void doSmaller(View view) {
589         nativeClearMessage();
590         nativeSmaller();
591     }
592 
593     // -----------------------------------------------------------------------------
594 
595     // called when the Scale=1:1 button is tapped
doScale1to1(View view)596     public void doScale1to1(View view) {
597         nativeClearMessage();
598         nativeScale1to1();
599     }
600 
601     // -----------------------------------------------------------------------------
602 
603     // called when the Bigger button is tapped
doBigger(View view)604     public void doBigger(View view) {
605         nativeClearMessage();
606         nativeBigger();
607     }
608 
609     // -----------------------------------------------------------------------------
610 
611     // called when the Middle button is tapped
doMiddle(View view)612     public void doMiddle(View view) {
613         nativeClearMessage();
614         nativeMiddle();
615     }
616 
617     // -----------------------------------------------------------------------------
618 
619     // called when the View button is tapped
doView(View view)620     public void doView(View view) {
621         nativeClearMessage();
622         // display pop-up menu with these items: Scale=1:1, Bigger, Smaller, Middle
623         popup = new PopupMenu(this, view);
624         MenuInflater inflater = popup.getMenuInflater();
625         inflater.inflate(R.menu.view_menu, popup.getMenu());
626         popup.show();
627     }
628 
629     // -----------------------------------------------------------------------------
630 
631     // called when item from view_menu is selected
doViewItem(MenuItem item)632     public void doViewItem(MenuItem item) {
633         popup.dismiss();
634         switch (item.getItemId()) {
635             case R.id.scale1to1: nativeScale1to1(); break;
636             case R.id.bigger:    nativeBigger(); break;
637             case R.id.smaller:   nativeSmaller(); break;
638             case R.id.middle:    nativeMiddle(); break;
639             default:             Log.e("Golly","Fix bug in doViewItem!");
640         }
641     }
642 
643     // -----------------------------------------------------------------------------
644 
645     // called when the Undo button is tapped
doUndo(View view)646     public void doUndo(View view) {
647         nativeClearMessage();
648         stopIfGenerating();
649         if (callAgainAfterDelay("doUndo", view, null)) return;
650         nativeUndo();
651         updateButtons();
652 
653     }
654 
655     // -----------------------------------------------------------------------------
656 
657     // called when the Redo button is tapped
doRedo(View view)658     public void doRedo(View view) {
659         nativeClearMessage();
660         // nativeIsGenerating() should never be true here
661         if (callAgainAfterDelay("doRedo", view, null)) return;
662         nativeRedo();
663         updateButtons();
664     }
665 
666     // -----------------------------------------------------------------------------
667 
668     // called when the All button is tapped
doSelectAll(View view)669     public void doSelectAll(View view) {
670         nativeClearMessage();
671         nativeSelectAll();
672         UpdateEditBar();
673     }
674 
675     // -----------------------------------------------------------------------------
676 
createSelectionMenu(MenuInflater inflater)677     private void createSelectionMenu(MenuInflater inflater) {
678         Menu menu = popup.getMenu();
679         inflater.inflate(R.menu.selection_menu, menu);
680         MenuItem item = menu.findItem(R.id.random);
681         item.setTitle("Random Fill (" + nativeGetRandomFill() + "%)");
682         if (widescreen) {
683             // delete the top 2 items
684             menu.removeItem(R.id.paste);
685             menu.removeItem(R.id.all);
686         }
687     }
688 
689     // -----------------------------------------------------------------------------
690 
691     // called when the Edit button is tapped (widescreen is true)
doEdit(View view)692     public void doEdit(View view) {
693         nativeClearMessage();
694         if (nativeSelectionExists()) {
695             popup = new PopupMenu(this, view);
696             MenuInflater inflater = popup.getMenuInflater();
697             createSelectionMenu(inflater);
698             popup.show();
699         } else {
700             // editbutton should be disabled if there's no selection
701             Log.e("Golly","Bug detected in doEdit!");
702         }
703     }
704 
705     // -----------------------------------------------------------------------------
706 
createPasteMenu(MenuInflater inflater)707     private void createPasteMenu(MenuInflater inflater) {
708         Menu menu = popup.getMenu();
709         inflater.inflate(R.menu.paste_menu, menu);
710         MenuItem item = menu.findItem(R.id.pastesel);
711         item.setEnabled(nativeSelectionExists());
712         item = menu.findItem(R.id.pastemode);
713         item.setTitle("Paste (" + nativeGetPasteMode() + ")");
714         if (nativeIsGenerating()) {
715             // probably best to stop generating when Paste button is tapped
716             // (consistent with iOS Golly)
717             stopped = true;
718             nativeStopGenerating();
719             updateButtons();
720         }
721     }
722 
723     // -----------------------------------------------------------------------------
724 
725     // called when the Paste button is tapped (widescreen is true)
doPaste(View view)726     public void doPaste(View view) {
727         nativeClearMessage();
728         if (nativePasteExists()) {
729             // show pop-up menu with various paste options
730             popup = new PopupMenu(this, view);
731             MenuInflater inflater = popup.getMenuInflater();
732             createPasteMenu(inflater);
733             popup.show();
734         } else {
735             // create the paste image
736             stopIfGenerating();
737             if (callAgainAfterDelay("doPaste", view, null)) return;
738             nativePaste();
739             UpdateEditBar();
740         }
741     }
742 
743     // -----------------------------------------------------------------------------
744 
745     // called when the Edit/Paste button is tapped (widescreen is false)
doEditPaste(View view)746     public void doEditPaste(View view) {
747         nativeClearMessage();
748         // display pop-up menu with items that depend on whether a selection or paste image exists
749         popup = new PopupMenu(this, view);
750         MenuInflater inflater = popup.getMenuInflater();
751         if (nativePasteExists()) {
752             createPasteMenu(inflater);
753         } else if (nativeSelectionExists()) {
754             createSelectionMenu(inflater);
755         } else {
756             inflater.inflate(R.menu.edit_menu, popup.getMenu());
757         }
758         popup.show();
759     }
760 
761     // -----------------------------------------------------------------------------
762 
763     // called when item from edit_menu is selected
doEditItem(MenuItem item)764     public void doEditItem(MenuItem item) {
765         if (item.getItemId() == R.id.paste) {
766             stopIfGenerating();
767             if (callAgainAfterDelay("doPaste", null, item)) return;
768         }
769         popup.dismiss();
770         switch (item.getItemId()) {
771             case R.id.paste: nativePaste(); break;
772             case R.id.all:   nativeSelectAll(); break;
773             default:         Log.e("Golly","Fix bug in doEditItem!");
774         }
775         UpdateEditBar();
776     }
777 
778     // -----------------------------------------------------------------------------
779 
780     // called when item from selection_menu is selected
doSelectionItem(MenuItem item)781     public void doSelectionItem(MenuItem item) {
782         if (item.getItemId() != R.id.remove &&
783             item.getItemId() != R.id.copy &&
784             item.getItemId() != R.id.shrink &&
785             item.getItemId() != R.id.fitsel) {
786             // item can modify the current pattern so we must stop generating,
787             // but nicer if we only stop temporarily and resume when done
788             if (nativeIsGenerating()) {
789                 // no need to set stopped = true
790                 nativePauseGenerating();
791             }
792             if (callAgainAfterDelay("doSelItem", null, item)) return;
793         }
794         popup.dismiss();
795         switch (item.getItemId()) {
796             // doEditItem handles the top 2 items (if widescreen is false)
797             // case R.id.paste: nativePaste(); break;
798             // case R.id.all:   nativeSelectAll(); break;
799             case R.id.remove:   nativeRemoveSelection(); break;
800             case R.id.cut:      nativeCutSelection(); break;
801             case R.id.copy:     nativeCopySelection(); break;
802             case R.id.clear:    nativeClearSelection(1); break;
803             case R.id.clearo:   nativeClearSelection(0); break;
804             case R.id.shrink:   nativeShrinkSelection(); break;
805             case R.id.fitsel:   nativeFitSelection(); break;
806             case R.id.random:   nativeRandomFill(); break;
807             case R.id.flipy:    nativeFlipSelection(1); break;
808             case R.id.flipx:    nativeFlipSelection(0); break;
809             case R.id.rotatec:  nativeRotateSelection(1); break;
810             case R.id.rotatea:  nativeRotateSelection(0); break;
811             case R.id.advance:  nativeAdvanceSelection(1); break;
812             case R.id.advanceo: nativeAdvanceSelection(0); break;
813             default:            Log.e("Golly","Fix bug in doSelectionItem!");
814         }
815         // resume generating (only if nativePauseGenerating was called)
816         nativeResumeGenerating();
817     }
818 
819     // -----------------------------------------------------------------------------
820 
821     // called when item from paste_menu is selected
doPasteItem(MenuItem item)822     public void doPasteItem(MenuItem item) {
823         popup.dismiss();
824         switch (item.getItemId()) {
825             case R.id.abort:     nativeAbortPaste(); break;
826             case R.id.pastemode: nativeDoPaste(0); break;
827             case R.id.pastesel:  nativeDoPaste(1); break;
828             case R.id.pflipy:    nativeFlipPaste(1); break;
829             case R.id.pflipx:    nativeFlipPaste(0); break;
830             case R.id.protatec:  nativeRotatePaste(1); break;
831             case R.id.protatea:  nativeRotatePaste(0); break;
832             default:             Log.e("Golly","Fix bug in doPasteItem!");
833         }
834         UpdateEditBar();
835     }
836 
837     // -----------------------------------------------------------------------------
838 
839     // called when the Draw/Pick/Select/Move button is tapped
doSetTouchMode(View view)840     public void doSetTouchMode(View view) {
841         nativeClearMessage();
842         // display pop-up menu with these items: Draw, Pick, Select, Move
843         popup = new PopupMenu(this, view);
844         MenuInflater inflater = popup.getMenuInflater();
845         inflater.inflate(R.menu.mode_menu, popup.getMenu());
846         popup.show();
847     }
848 
849     // -----------------------------------------------------------------------------
850 
851     // called when item from mode_menu is selected
doModeItem(MenuItem item)852     public void doModeItem(MenuItem item) {
853         popup.dismiss();
854         switch (item.getItemId()) {
855             case R.id.draw:   nativeSetMode(0); break;
856             case R.id.pick:   nativeSetMode(1); break;
857             case R.id.select: nativeSetMode(2); break;
858             case R.id.move:   nativeSetMode(3); break;
859             default:          Log.e("Golly","Fix bug in doModeItem!");
860         }
861         UpdateEditBar();      // update modebutton text
862     }
863 
864     // -----------------------------------------------------------------------------
865 
866     // called when the Draw button is tapped
doDrawMode(View view)867     public void doDrawMode(View view) {
868         nativeClearMessage();
869         if (nativeGetMode() != 0) {
870             nativeSetMode(0);
871             UpdateEditBar();
872         }
873     }
874 
875     // -----------------------------------------------------------------------------
876 
877     // called when the Pick button is tapped
doPickMode(View view)878     public void doPickMode(View view) {
879         nativeClearMessage();
880         if (nativeGetMode() != 1) {
881             nativeSetMode(1);
882             UpdateEditBar();
883         }
884     }
885 
886     // -----------------------------------------------------------------------------
887 
888     // called when the Select button is tapped
doSelectMode(View view)889     public void doSelectMode(View view) {
890         nativeClearMessage();
891         if (nativeGetMode() != 2) {
892             nativeSetMode(2);
893             UpdateEditBar();
894         }
895     }
896 
897     // -----------------------------------------------------------------------------
898 
899     // called when the Move button is tapped
doMoveMode(View view)900     public void doMoveMode(View view) {
901         nativeClearMessage();
902         if (nativeGetMode() != 3) {
903             nativeSetMode(3);
904             UpdateEditBar();
905         }
906     }
907 
908     // -----------------------------------------------------------------------------
909 
910     // called when the state button is tapped
doChangeState(View view)911     public void doChangeState(View view) {
912         nativeClearMessage();
913         // let user change the current drawing state
914         Intent intent = new Intent(this, StateActivity.class);
915         startActivity(intent);
916     }
917 
918     // -----------------------------------------------------------------------------
919 
920     // called when the New button is tapped
doNewPattern(View view)921     public void doNewPattern(View view) {
922         nativeClearMessage();
923         // stopIfGenerating() would work here but we use smarter code that
924         // avoids trying to save the current pattern (potentially very large)
925         if (nativeIsGenerating()) {
926             stopped = true;
927             nativeStopBeforeNew();
928         }
929         if (callAgainAfterDelay("doNew", view, null)) return;
930 
931         nativeNewPattern();
932         updateButtons();
933         UpdateEditBar();
934 
935         if (nativeNumLayers() == 1) {
936             // delete all files in tempdir
937             deleteTempFiles();
938         }
939     }
940 
941     // -----------------------------------------------------------------------------
942 
943     // called when the Rule button is tapped
doRule(View view)944     public void doRule(View view) {
945         nativeClearMessage();
946         stopIfGenerating();
947         if (callAgainAfterDelay("doRule", view, null)) return;
948 
949         // let user change the current rule and/or algorithm
950         Intent intent = new Intent(this, RuleActivity.class);
951         startActivity(intent);
952     }
953 
954     // -----------------------------------------------------------------------------
955 
956     // called when the Info button is tapped
doInfo(View view)957     public void doInfo(View view) {
958         nativeClearMessage();
959         stopIfGenerating();
960         if (callAgainAfterDelay("doInfo", view, null)) return;
961 
962         // display any comments in current pattern file
963         Intent intent = new Intent(this, InfoActivity.class);
964         intent.putExtra(InfoActivity.INFO_MESSAGE, "native");
965         startActivity(intent);
966     }
967 
968     // -----------------------------------------------------------------------------
969 
970     // called when the Save button is tapped
doSave(View view)971     public void doSave(View view) {
972         nativeClearMessage();
973         stopIfGenerating();
974         if (callAgainAfterDelay("doSave", view, null)) return;
975 
976         AlertDialog.Builder alert = new AlertDialog.Builder(this);
977         alert.setTitle("Save current pattern");
978         alert.setMessage("Valid file name extensions are\n" + nativeGetValidExtensions());
979 
980         // or use radio buttons as in iPad Golly???
981         // might be better to create a new SaveActivity and make it appear in a dialog
982         // by setting its theme to Theme.Holo.Dialog in the <activity> manifest element
983 
984         // use an EditText view to get filename
985         final EditText input = new EditText(this);
986         input.setSingleLine(true);
987         input.setHint("enter a file name");
988         input.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
989         alert.setView(input);
990 
991         alert.setPositiveButton("SAVE",
992             new DialogInterface.OnClickListener() {
993                 public void onClick(DialogInterface dialog, int id) {
994                     // do nothing here (see below)
995                 }
996             });
997         alert.setNegativeButton("CANCEL",
998             new DialogInterface.OnClickListener() {
999                 public void onClick(DialogInterface dialog, int id) {
1000                     dialog.cancel();
1001                 }
1002             });
1003 
1004         // don't call alert.show() here -- instead we use the following stuff
1005         // which allows us to prevent the alert dialog from closing if the
1006         // given file name is invalid
1007 
1008         final AlertDialog dialog = alert.create();
1009         dialog.show();
1010         dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
1011             new View.OnClickListener() {
1012                 public void onClick(View v) {
1013                     String filename = input.getText().toString();
1014                     if (filename.length() == 0) {
1015                         PlayBeepSound();
1016                         return;
1017                     }
1018                     BaseApp baseapp = (BaseApp)getApplicationContext();
1019                     // check for valid extension
1020                     if (!nativeValidExtension(filename)) {
1021                         baseapp.Warning("Invalid file extension.");
1022                         return;
1023                     }
1024                     // check if given file already exists
1025                     if (nativeFileExists(filename)) {
1026                         String answer = baseapp.YesNo("A file with that name already exists.  Do you want to replace that file?");
1027                         if (answer.equals("no")) return;
1028                     }
1029                      // dismiss dialog first in case SavePattern calls BeginProgress
1030                     dialog.dismiss();
1031                     nativeSavePattern(filename);
1032                 }
1033             });
1034     }
1035 
1036     // -----------------------------------------------------------------------------
1037 
1038     // called when the Full/Restore button is tapped
toggleFullScreen(View view)1039     public void toggleFullScreen(View view) {
1040         // no need to call nativeClearMessage()
1041         LinearLayout topbar = (LinearLayout) findViewById(R.id.top_bar);
1042         LinearLayout editbar = (LinearLayout) findViewById(R.id.edit_bar);
1043         RelativeLayout bottombar = (RelativeLayout) findViewById(R.id.bottom_bar);
1044 
1045         if (fullscreen) {
1046             fullscreen = false;
1047             restorebutton.setVisibility(View.INVISIBLE);
1048 
1049             status1.setVisibility(View.VISIBLE);
1050             status2.setVisibility(View.VISIBLE);
1051             status3.setVisibility(View.VISIBLE);
1052 
1053             topbar.setVisibility(LinearLayout.VISIBLE);
1054             editbar.setVisibility(LinearLayout.VISIBLE);
1055             bottombar.setVisibility(RelativeLayout.VISIBLE);
1056 
1057             getActionBar().show();
1058         } else {
1059             fullscreen = true;
1060             restorebutton.setVisibility(View.VISIBLE);
1061 
1062             status1.setVisibility(View.GONE);
1063             status2.setVisibility(View.GONE);
1064             status3.setVisibility(View.GONE);
1065 
1066             topbar.setVisibility(LinearLayout.GONE);
1067             editbar.setVisibility(LinearLayout.GONE);
1068             bottombar.setVisibility(RelativeLayout.GONE);
1069 
1070             getActionBar().hide();
1071         }
1072 
1073         // need to let native code know (this will update status bar if not fullscreen)
1074         nativeSetFullScreen(fullscreen);
1075 
1076         if (!fullscreen) {
1077             updateButtons();
1078             UpdateEditBar();
1079         }
1080     }
1081 
1082     // -----------------------------------------------------------------------------
1083 
1084     // this method is called from C++ code (see jnicalls.cpp)
StartMainActivity()1085     private void StartMainActivity() {
1086         Intent intent = new Intent(this, MainActivity.class);
1087         startActivity(intent);
1088     }
1089 
1090     // -----------------------------------------------------------------------------
1091 
1092     // this method is called from C++ code (see jnicalls.cpp)
RefreshPattern()1093     private void RefreshPattern() {
1094         // this can be called from any thread
1095         pattView.requestRender();
1096     }
1097 
1098     // -----------------------------------------------------------------------------
1099 
1100     // this method is called from C++ code (see jnicalls.cpp)
ShowStatusLines()1101     private void ShowStatusLines() {
1102         // no need to check fullscreen flag here (caller checks it)
1103 
1104         // this might be called from a non-UI thread
1105         runOnUiThread(new Runnable() {
1106             public void run() {
1107                 int bgcolor = nativeGetStatusColor();
1108                 if (statuscolor != bgcolor) {
1109                     // change background color of status lines
1110                     statuscolor = bgcolor;
1111                     status1.setBackgroundColor(statuscolor);
1112                     status2.setBackgroundColor(statuscolor);
1113                     status3.setBackgroundColor(statuscolor);
1114                 }
1115                 status1.setText(nativeGetStatusLine(1));
1116                 status2.setText(nativeGetStatusLine(2));
1117                 status3.setText(nativeGetStatusLine(3));
1118             }
1119         });
1120     }
1121 
1122     // -----------------------------------------------------------------------------
1123 
1124     // this method is called from C++ code (see jnicalls.cpp)
UpdateEditBar()1125     private void UpdateEditBar() {
1126         if (fullscreen) return;
1127 
1128         // this might be called from a non-UI thread
1129         runOnUiThread(new Runnable() {
1130             public void run() {
1131                 undobutton.setEnabled(nativeCanUndo());
1132                 redobutton.setEnabled(nativeCanRedo());
1133 
1134                 // update Edit button
1135                 if (widescreen) {
1136                     editbutton.setEnabled(nativeSelectionExists());
1137                 } else {
1138                     if (nativePasteExists()) {
1139                         editbutton.setText("Paste");
1140                     } else {
1141                         editbutton.setText("Edit");
1142                     }
1143                 }
1144 
1145                 // show current drawing state
1146                 statebutton.setText("State=" + Integer.toString(nativeDrawingState()));
1147 
1148                 // show current touch mode
1149                 if (widescreen) {
1150                     drawbutton.setTypeface(Typeface.DEFAULT);
1151                     pickbutton.setTypeface(Typeface.DEFAULT);
1152                     selectbutton.setTypeface(Typeface.DEFAULT);
1153                     movebutton.setTypeface(Typeface.DEFAULT);
1154                     drawbutton.setTextColor(Color.rgb(0,0,0));
1155                     pickbutton.setTextColor(Color.rgb(0,0,0));
1156                     selectbutton.setTextColor(Color.rgb(0,0,0));
1157                     movebutton.setTextColor(Color.rgb(0,0,0));
1158                     switch (nativeGetMode()) {
1159                         case 0:  drawbutton.setTypeface(Typeface.DEFAULT_BOLD);
1160                                  drawbutton.setTextColor(Color.rgb(0,0,255));
1161                                  break;
1162                         case 1:  pickbutton.setTypeface(Typeface.DEFAULT_BOLD);
1163                                  pickbutton.setTextColor(Color.rgb(0,0,255));
1164                                  break;
1165                         case 2:  selectbutton.setTypeface(Typeface.DEFAULT_BOLD);
1166                                  selectbutton.setTextColor(Color.rgb(0,0,255));
1167                                  break;
1168                         case 3:  movebutton.setTypeface(Typeface.DEFAULT_BOLD);
1169                                  movebutton.setTextColor(Color.rgb(0,0,255));
1170                                  break;
1171                         default: Log.e("Golly","Fix bug in UpdateEditBar!");
1172                     }
1173                 } else {
1174                     switch (nativeGetMode()) {
1175                         case 0:  modebutton.setText("Draw"); break;
1176                         case 1:  modebutton.setText("Pick"); break;
1177                         case 2:  modebutton.setText("Select"); break;
1178                         case 3:  modebutton.setText("Move"); break;
1179                         default: Log.e("Golly","Fix bug in UpdateEditBar!");
1180                     }
1181                 }
1182             }
1183         });
1184     }
1185 
1186     // -----------------------------------------------------------------------------
1187 
1188     // this method is called from C++ code (see jnicalls.cpp)
PlayBeepSound()1189     private void PlayBeepSound() {
1190         ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
1191         if (tg != null) {
1192             tg.startTone(ToneGenerator.TONE_PROP_BEEP);
1193             tg.release();
1194         }
1195     }
1196 
1197     // -----------------------------------------------------------------------------
1198 
1199     // this method is called from C++ code (see jnicalls.cpp)
RemoveFile(String filepath)1200     private void RemoveFile(String filepath) {
1201         File file = new File(filepath);
1202         if (!file.delete()) {
1203             Log.e("RemoveFile failed", filepath);
1204         }
1205     }
1206 
1207     // -----------------------------------------------------------------------------
1208 
1209     // this method is called from C++ code (see jnicalls.cpp)
MoveFile(String oldpath, String newpath)1210     private String MoveFile(String oldpath, String newpath) {
1211         String result = "";
1212         File oldfile = new File(oldpath);
1213         File newfile = new File(newpath);
1214         if (!oldfile.renameTo(newfile)) {
1215             Log.e("MoveFile failed: old", oldpath);
1216             Log.e("MoveFile failed: new", newpath);
1217             result = "MoveFile failed";
1218         }
1219         return result;
1220     }
1221 
1222     // -----------------------------------------------------------------------------
1223 
1224     // this method is called from C++ code (see jnicalls.cpp)
CopyTextToClipboard(String text)1225     private void CopyTextToClipboard(String text) {
1226         ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
1227         ClipData clip = ClipData.newPlainText("RLE data", text);
1228         clipboard.setPrimaryClip(clip);
1229         // Log.i("CopyTextToClipboard", text);
1230     }
1231 
1232     // -----------------------------------------------------------------------------
1233 
1234     // this method is called from C++ code (see jnicalls.cpp)
GetTextFromClipboard()1235     private String GetTextFromClipboard() {
1236         BaseApp baseapp = (BaseApp)getApplicationContext();
1237         ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
1238         String text = "";
1239         if (clipboard.hasPrimaryClip()) {
1240             ClipData clipData = clipboard.getPrimaryClip();
1241             text = clipData.getItemAt(0).coerceToText(this).toString();
1242             if (text.length() == 0) {
1243             	baseapp.Warning("Failed to get text from clipboard!");
1244             }
1245         }
1246         // Log.i("GetTextFromClipboard", text);
1247         return text;
1248     }
1249 
1250     // -----------------------------------------------------------------------------
1251 
1252     // this method is called from C++ code (see jnicalls.cpp)
ShowTextFile(String filepath)1253     private void ShowTextFile(String filepath) {
1254         BaseApp baseapp = (BaseApp)getApplicationContext();
1255         // display file contents
1256         Intent intent = new Intent(baseapp.getCurrentActivity(), InfoActivity.class);
1257         intent.putExtra(InfoActivity.INFO_MESSAGE, filepath);
1258         startActivity(intent);
1259     }
1260 
1261     // -----------------------------------------------------------------------------
1262 
1263     // this method is called from C++ code (see jnicalls.cpp)
ShowHelp(String filepath)1264     private void ShowHelp(String filepath) {
1265         Intent intent = new Intent(this, HelpActivity.class);
1266         intent.putExtra(HelpActivity.SHOWHELP_MESSAGE, filepath);
1267         startActivity(intent);
1268     }
1269 
1270     // -----------------------------------------------------------------------------
1271 
1272     // this method is called from C++ code (see jnicalls.cpp)
BeginProgress(String title)1273     private void BeginProgress(String title) {
1274         if (progresscount == 0) {
1275             // can we disable interaction with all views outside proglayout???
1276             progtitle.setText(title);
1277             progbar.setProgress(0);
1278             // proglayout.setVisibility(LinearLayout.VISIBLE);
1279             cancelProgress = false;
1280             progstart = System.nanoTime();
1281         }
1282         progresscount++;    // handles nested calls
1283     }
1284 
1285     // -----------------------------------------------------------------------------
1286 
1287     // this method is called from C++ code (see jnicalls.cpp)
AbortProgress(int percentage, String message)1288     private boolean AbortProgress(int percentage, String message) {
1289         if (progresscount <= 0) {
1290             Log.e("Golly","Bug detected in AbortProgress!");
1291             return true;
1292         }
1293         long nanosecs = System.nanoTime() - progstart;
1294         if (proglayout.getVisibility() == LinearLayout.VISIBLE) {
1295             if (nanosecs < prognext) return false;
1296             prognext = nanosecs + 100000000L;     // update progress bar about 10 times per sec
1297             updateProgressBar(percentage);
1298             return cancelProgress;
1299         } else {
1300             // note that percentage is not always an accurate estimator for how long
1301             // the task will take, especially when we use nextcell for cut/copy
1302             if ( (nanosecs > 1000000000L && percentage < 30) || nanosecs > 2000000000L ) {
1303                 // task is probably going to take a while so show progress bar
1304                 proglayout.setVisibility(LinearLayout.VISIBLE);
1305                 updateProgressBar(percentage);
1306             }
1307             prognext = nanosecs + 10000000L;     // 0.01 sec delay until 1st progress update
1308         }
1309         return false;
1310     }
1311 
1312     // -----------------------------------------------------------------------------
1313 
updateProgressBar(int percentage)1314     private void updateProgressBar(int percentage) {
1315         if (percentage < 0 || percentage > 100) return;
1316         progbar.setProgress(percentage);
1317         CheckMessageQueue();
1318     }
1319 
1320     // -----------------------------------------------------------------------------
1321 
1322     // this method is called from C++ code (see jnicalls.cpp)
EndProgress()1323     private void EndProgress() {
1324         if (progresscount <= 0) {
1325             Log.e("Golly","Bug detected in EndProgress!");
1326             return;
1327         }
1328         progresscount--;
1329         if (progresscount == 0) {
1330             proglayout.setVisibility(LinearLayout.INVISIBLE);
1331         }
1332     }
1333 
1334     // -----------------------------------------------------------------------------
1335 
1336     // called when the Cancel button is tapped
doCancel(View view)1337     public void doCancel(View view) {
1338         cancelProgress = true;
1339     }
1340 
1341 } // MainActivity class
1342