/* * This file is part of Oekaki Mobile. * Copyright (C) 2013 Jeremy Lam * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package idv.jlchntoz.oekakimobile; import java.io.*; import java.util.ArrayList; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.*; import android.graphics.drawable.*; import android.net.Uri; import android.os.*; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.view.View.*; import android.widget.Toast; import com.google.analytics.tracking.android.EasyTracker; import com.actionbarsherlock.app.*; import com.actionbarsherlock.view.*; import com.actionbarsherlock.view.MenuItem.*; import com.chibipaint.*; import com.chibipaint.CPController.*; import com.chibipaint.engine.*; import com.chibipaint.util.*; import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu; public class MainActivity extends SherlockActivity implements OnMenuItemClickListener, ICPToolListener, ICPModeListener, ICPColorListener, ICPEventListener, ICPViewListener, SlidingMenu.OnCloseListener, SlidingMenu.OnOpenListener, OnTouchListener { public static final int MSG_UPDATECOLOR = 2; public static final int MSG_SETARTWORK = 3; public static final int MSG_SHOWFILEERROR = 4; final String extFilePath = Environment.getExternalStorageDirectory() .getPath(); String fileName; PaintCanvas PC; public SlidingMenu drawer; brushSettingsDialog BSD; ProgressDialog LoadingDialog; Exception _error; int penChecked; SubMenu pensMenu; ArrayList<MenuItem> customPenMenuItems; int modeChecked; MenuItem[] modeMenuItems; MenuItem colorMainMenuItem, colorMenuItem, brushSettingsMenuItem, textureSettingsMenuItem; LayerDrawerHandler drawerHandler1; ColorTextureDrawerHandler drawerHandler2; ActionMode drawerActionMode; CPBrushInfo Cinfo; CPController controller; CPArtwork artwork; ArrayList<CPBrushInfo> customPens; BitmapDrawable colorIcon; Bitmap colorIconBase; Canvas ColorIconCanvas; int colorPicked; savedSettings settings; boolean doubleBackToExitPressedOnce; public MainActivity() { super(); customPenMenuItems = new ArrayList<MenuItem>(); customPens = new ArrayList<CPBrushInfo>(); } @SuppressWarnings("static-access") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_ACTION_BAR); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); setContentView(R.layout.activity_main); getSupportActionBar().setHomeButtonEnabled(true); settings = new savedSettings(this); settings.getCustomPens(customPens); penChecked = 0; modeMenuItems = new MenuItem[controller.M_MAX]; drawer = new SlidingMenu(this); drawer.setTouchModeAbove(SlidingMenu.TOUCHMODE_NONE); drawer.setShadowWidthRes(R.dimen.shadow_width); drawer.setBehindOffsetRes(R.dimen.slidingmenu_offset); drawer.setFadeDegree(0.35f); drawer.setBehindWidthRes(R.dimen.drawer_size); drawer.setShadowDrawable(R.drawable.shadow); drawer.setMenu(R.layout.drawer_content); drawer.setSecondaryShadowDrawable(R.drawable.shadow_r); drawer.setSecondaryMenu(R.layout.drawer_content2); drawer.setOnOpenListener(this); drawer.setOnCloseListener(this); drawer.attachToActivity(this, SlidingMenu.SLIDING_CONTENT); PC = (PaintCanvas) findViewById(R.id.cbpaintcanvas); PC.setOnTouchListener(this); controller = PC.controller; Intent currentIntent = getIntent(); if (currentIntent.hasExtra("file")) fileName = currentIntent.getStringExtra("file"); else if (currentIntent.getData() != null && !currentIntent.hasExtra("notfirstrun")) fileName = currentIntent.getData().getEncodedPath(); if (fileName != null) { LoadingDialog = ProgressDialog.show(this, "", getResources().getString(R.string.loading), false, false); final File file = new File(fileName); setTitle(file.getName()); new Thread() { @Override public void run() { try { if (file.exists()) { FileInputStream FIS = new FileInputStream(file); artwork = CPChibiFile.read(MainActivity.this, FIS); FIS.close(); Message m = new Message(); m.what = MSG_SETARTWORK; _h.sendMessage(m); } } catch (Exception e) { _error = e; Message m = new Message(); m.what = MSG_SHOWFILEERROR; _h.sendMessage(m); } } }.start(); } else { int width = 800, height = 600; if (currentIntent.hasExtra("width")) width = currentIntent.getIntExtra("width", 800); if (currentIntent.hasExtra("height")) height = currentIntent.getIntExtra("height", 600); artwork = new CPArtwork(this, width, height); setArtwork(artwork); initController(); fileName = ""; } } @SuppressLint("HandlerLeak") private final Handler _h = new Handler() { @Override public void handleMessage(Message m) { switch (m.what) { case MSG_UPDATECOLOR: if (drawerHandler2 == null) break; colorIconBase = drawerHandler2.getBitmap(); colorIcon = new BitmapDrawable(getResources(), colorIconBase); colorMainMenuItem.setIcon(colorIcon); break; case MSG_SETARTWORK: setArtwork(artwork); initController(); if (LoadingDialog != null && LoadingDialog.isShowing()) LoadingDialog.dismiss(); break; case MSG_SHOWFILEERROR: Toast.makeText(MainActivity.this, getString(R.string.fileioerror), Toast.LENGTH_LONG).show(); if (LoadingDialog != null && LoadingDialog.isShowing()) LoadingDialog.dismiss(); break; } } }; @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getSupportMenuInflater().inflate(R.menu.main, menu); pensMenu = menu.addSubMenu(getString(R.string.tools)); refreshPensMenu(); pensMenu .getItem() .setIcon(R.drawable.ic_menu_tools) .setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); SubMenu colorMenu = menu.addSubMenu(getString(R.string.colortexture)) .setIcon(colorIcon); colorMainMenuItem = colorMenu.getItem(); colorMainMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); colorMainMenuItem.setOnMenuItemClickListener(this); newColor(controller.getCurColor()); return true; } private void refreshPensMenu() { runOnUiThread(r_RefreshPensMenu); } private Runnable r_RefreshPensMenu = new Runnable() { @Override public void run() { pensMenu.clear(); customPenMenuItems.clear(); modeMenuItems[CPController.M_MOVE_TOOL] = pensMenu.add(2, Menu.NONE, Menu.NONE, getString(R.string.move)).setOnMenuItemClickListener( MainActivity.this); modeMenuItems[CPController.M_MOVE_CANVAS] = pensMenu.add(2, Menu.NONE, Menu.NONE, getString(R.string.movecanvas)) .setOnMenuItemClickListener(MainActivity.this); modeMenuItems[CPController.M_RECT_SELECTION] = pensMenu.add(2, Menu.NONE, Menu.NONE, getString(R.string.rectselection)) .setOnMenuItemClickListener(MainActivity.this); modeMenuItems[CPController.M_COLOR_PICKER] = pensMenu.add(2, Menu.NONE, Menu.NONE, getString(R.string.colorpicker)) .setOnMenuItemClickListener(MainActivity.this); modeMenuItems[CPController.M_FLOODFILL] = pensMenu.add(2, Menu.NONE, Menu.NONE, getString(R.string.floodfill)).setOnMenuItemClickListener( MainActivity.this); for (int i = 0; i < modeMenuItems.length; i++) if (modeMenuItems[i] != null) modeMenuItems[i].setChecked(i == modeChecked); for (int i = 0; i < customPens.size(); i++) { while (customPenMenuItems.size() <= i) customPenMenuItems.add(null); if (customPens.get(i) != null) customPenMenuItems.set( i, pensMenu .add(2, Menu.NONE, Menu.NONE, customPens.get(i).getName()) .setChecked( penChecked == i && modeChecked == CPController.M_DRAW) .setOnMenuItemClickListener(MainActivity.this)); } pensMenu.setGroupCheckable(1, true, false); pensMenu.setGroupCheckable(2, true, false); brushSettingsMenuItem = pensMenu.add(3, Menu.NONE, Menu.NONE, getString(R.string.brushsettings)).setOnMenuItemClickListener( MainActivity.this); } }; @Override public boolean onOptionsItemSelected(MenuItem item) { return onMenuItemClick(item) ? true : super.onOptionsItemSelected(item); } @Override public boolean onMenuItemClick(MenuItem item) { if (colorMainMenuItem == item) { drawer.setMode(SlidingMenu.RIGHT); drawerHandler1.drawerView.setVisibility(View.INVISIBLE); drawerHandler2.drawerView.setVisibility(View.VISIBLE); if (drawer.isMenuShowing()) drawer.showContent(); if (drawer.isSecondaryMenuShowing()) drawer.showContent(); else drawer.showSecondaryMenu(); } else if (brushSettingsMenuItem == item) BSD.showDialog(); else switch (item.getItemId()) { case android.R.id.home: drawer.setMode(SlidingMenu.LEFT); drawerHandler1.drawerView.setVisibility(View.VISIBLE); drawerHandler2.drawerView.setVisibility(View.INVISIBLE); if (drawer.isSecondaryMenuShowing()) drawer.showContent(); if (drawer.isMenuShowing()) drawer.showContent(); else drawer.showMenu(); break; case R.id.menu_edit: drawerActionMode = startActionMode(new EditActionMode(this, controller)); break; case R.id.menu_new: onNewClick(); break; case R.id.menu_clear: restartAndOpenWith(artwork.width, artwork.height); break; case R.id.menu_open: FileDialog openDlg = new FileDialog(this, getString(R.string.open), extFilePath + File.separator + "mypaint", ".chi", new String[] { "chi" }, true, new FileDialog.FileDialogCallBack() { @Override public void onCallBack(FileDialog which, File file) { restartAndOpenWith(file); } }); openDlg.showDialog(); break; case R.id.menu_save: onSaveClick(false); break; case R.id.menu_saveas: onSaveAsClick(false); break; case R.id.menu_share: shareImage(fileName); break; case R.id.menu_hfilp: artwork.hFlip(); break; case R.id.menu_vflip: artwork.vFlip(); break; case R.id.menu_invert: artwork.invert(); break; case R.id.menu_cnoise: artwork.colorNoise(); break; case R.id.menu_noise: artwork.monochromaticNoise(); break; case R.id.menu_boxblur: BlurDialog bbdlg = new BlurDialog(this, getString(R.string.boxblur), 3, 3, 3, new BlurDialog.BlurDialogCallBack() { @Override public void onCallBack(BlurDialog which, int width, int height, int iterations) { artwork.boxBlur(width, height, iterations); } }); bbdlg.showDialog(); break; case R.id.menu_fullscreen: toggleStatusBar(getWindow(), false); getSupportActionBar().hide(); break; case R.id.menu_about: AboutBox _ab = new AboutBox(this); _ab.showDialog(); break; default: int i = customPenMenuItems.indexOf(item); if (i != -1) { controller.setTool(customPens.get(i)); return true; } for (i = 0; i < modeMenuItems.length; i++) if (modeMenuItems[i] == item) { controller.setMode(i); return true; } return false; } return true; } public void onNewClick() { SizeDialog nfdlg = new SizeDialog(this, getString(R.string.newfile), artwork.width, artwork.height, new SizeDialog.SizeDialogCallBack() { @Override public void onCallBack(SizeDialog which, int width, int height) { restartAndOpenWith(width, height); } }); nfdlg.showDialog(); } public void onSaveClick(Boolean exitOnDone) { if (fileName != null && fileName != "") saveFile(new File(fileName), exitOnDone); else onSaveAsClick(exitOnDone); } public void onSaveAsClick(final Boolean exitOnDone) { FileDialog saveDlg = new FileDialog(this, getString(R.string.save), extFilePath + File.separator + "mypaint", new File(fileName).getName(), new String[] { "chi" }, false, new FileDialog.FileDialogCallBack() { @Override public void onCallBack(FileDialog which, File file) { saveFile(file, exitOnDone); } }); saveDlg.showDialog(); } @Override public boolean onPrepareOptionsMenu(Menu menu) { exitFullScreen(); return true; } private void exitFullScreen() { if (!getSupportActionBar().isShowing()) { getSupportActionBar().show(); toggleStatusBar(getWindow(), true); } } private void toggleStatusBar(android.view.Window w, boolean show) { WindowManager.LayoutParams attrs = w.getAttributes(); if (show) attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; else attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; w.setAttributes(attrs); } @Override public void modeChange(int mode) { modeChecked = mode; refreshPensMenu(); } @Override public void onOpen() { if (drawer.getMode() == SlidingMenu.LEFT) { onClose(); drawerActionMode = startActionMode(new LayersActionMode(this, controller)); } } @Override public void onClose() { if (drawerActionMode != null) drawerActionMode.finish(); } @Override public boolean onTouch(View v, MotionEvent e) { if (v == PC) onClose(); return false; } @Override public void onStart() { super.onStart(); EasyTracker.getInstance(this).activityStart(this); if(!getIntent().hasExtra("notfirstrun") && fileName == "") onNewClick(); } @Override public void onStop() { super.onStop(); EasyTracker.getInstance(this).activityStop(this); } @Override public void newTool(int tool, CPBrushInfo toolInfo) { if (tool < 0) toolInfo.toolNb = tool = customPens.size(); penChecked = tool; Cinfo = toolInfo; if (toolInfo.getName() != "") { while (tool >= customPens.size()) customPens.add(null); customPens.set(tool, toolInfo); } refreshPensMenu(); } @Override public void viewChange(CPViewInfo viewInfo) { } @Override public void cpEvent() { android.util.Log.d("CPEVENT", "CPEVENT CALLED"); } @Override public void newColor(CPColor color) { colorPicked = color.rgb | 0xFF << 24; Message m = new Message(); m.what = MSG_UPDATECOLOR; _h.sendMessage(m); } public void rePaint() { PC.repaint(); } private void setArtwork(CPArtwork newArtWork) { PC.setArtWork(newArtWork); PC.invalidate(); controller.setTool(customPens.get(penChecked)); artwork.callListenersLayerChange(); } private void saveFile(final File sfile, final Boolean exitOnDone) { LoadingDialog = ProgressDialog.show(this, "", getResources().getString(R.string.loading), false, false); new Thread() { @Override public void run() { try { File file = sfile; if (!file.getName().endsWith(".chi")) file = new File(file.getPath() + ".chi"); file.getParentFile().mkdirs(); if (!file.exists()) file.createNewFile(); FileOutputStream FOS = new FileOutputStream(file); CPChibiFile.write(FOS, artwork); FOS.close(); fileName = file.getPath(); outputImage(fileName); artwork.clearHistory(); LoadingDialog.dismiss(); if(exitOnDone) finish(); } catch (Exception ex) { _error = ex; Message m = new Message(); m.what = MSG_SHOWFILEERROR; _h.sendMessage(m); } } }.start(); } private String outputImage(String fileName) { Bitmap bm = Bitmap.createBitmap(artwork.width, artwork.height, Bitmap.Config.ARGB_8888); bm.setPixels(artwork.getDisplayBM().data, 0, artwork.width, 0, 0, artwork.width, artwork.height); ByteArrayOutputStream BAOS = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, BAOS); if (fileName == null || fileName == "") fileName = extFilePath + "/mypaint/share.png"; else if (!fileName.endsWith(".png")) fileName += ".png"; try { File file = new File(fileName); file.getParentFile().mkdirs(); if (!file.exists()) file.createNewFile(); FileOutputStream FOS = new FileOutputStream(file); FOS.write(BAOS.toByteArray()); FOS.close(); } catch (Exception ex) { ex.printStackTrace(); _error = ex; Message m = new Message(); m.what = MSG_SHOWFILEERROR; _h.sendMessage(m); return ""; } return fileName; } private void shareImage(final String fileName) { LoadingDialog = ProgressDialog.show(this, "", getResources().getString(R.string.loading), false, false); new Thread() { @Override public void run() { String _fileName = outputImage(fileName); if (_fileName == "") return; Intent share = new Intent(Intent.ACTION_SEND); share.setType("image/png"); share.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + _fileName)); startActivity(Intent.createChooser(share, getString(R.string.share))); LoadingDialog.dismiss(); } }.start(); } private void initController() { BSD = new brushSettingsDialog(this, controller); drawerHandler1 = new LayerDrawerHandler(this, controller, drawer.getMenu()); drawerHandler2 = new ColorTextureDrawerHandler(this, controller, drawer.getSecondaryMenu()); controller.setCurColor(new CPColor(getResources().getColor( R.color.DefaultColor))); controller.addToolListener(this); controller.addColorListener(this); controller.addCPEventListener(this); controller.addModeListener(this); controller.addViewListener(this); } private void restartAndOpenWith(int width, int height) { Intent i = getIntent(); if (i.hasExtra("file")) i.removeExtra("file"); i.putExtra("width", width); i.putExtra("height", height); i.putExtra("notfirstrun", true); finish(); startActivity(i); } private void restartAndOpenWith(File file) { Intent i = getIntent(); if (i.hasExtra("width")) i.removeExtra("width"); if (i.hasExtra("height")) i.removeExtra("height"); i.putExtra("file", file.getPath()); i.putExtra("notfirstrun", true); finish(); startActivity(i); } @Override public void onBackPressed() { exitFullScreen(); if (drawer.isSecondaryMenuShowing() || drawer.isMenuShowing()) { drawer.showContent(); return; } if(artwork.canUndo()) { new AlertDialog.Builder(this) .setIcon(android.R.drawable.ic_dialog_alert) .setTitle(android.R.string.dialog_alert_title) .setMessage(R.string.savereq) .setPositiveButton(R.string.save, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface d, int w) { onSaveClick(true); } }) .setNeutralButton(R.string.saveas,new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface d, int w) { onSaveAsClick(true); } }) .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface d, int w) { finish(); } }) .show(); return; } if (doubleBackToExitPressedOnce) { super.onBackPressed(); return; } doubleBackToExitPressedOnce = true; Toast.makeText(this, R.string.backagaintoexit, Toast.LENGTH_SHORT).show(); new Handler().postDelayed(new Runnable() { @Override public void run() { doubleBackToExitPressedOnce = false; } }, 2000); } public savedSettings getSettings() { return settings; } }