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