Calsign / APDE

Source code for APDE: Create and run Processing sketches on an Android device.
GNU General Public License v2.0
338 stars 76 forks source link

Bug/Faq: LoadXYZ from subfolders in preview not working #88

Open uheinema opened 4 years ago

uheinema commented 4 years ago

Puzzled me for a year, see below. Maybe this should be added to the FAQ.


// BUG/Workaround: APDE Preview can not load assets from subdirs

// To demonstrate, create a textfile
// sketch/data/I/i.exist
// due to a bug(?) in PApplet/PreviewUtil interaction
// this can only be loaded by an app, not a preview in APDE.
// nb: files in data/ itself always work
//
// How do they get there? And why not subdirs?
// By some magic (to be explained by calsign :)
// In PreviewActivity.java the assets are
// copied to local storage context.getFilesDir()) by
// PreviewUtil.copyAssets(this, dataFolder);
// ...and it DOES copy
// subdirs, and File(sketchPath("") +"/"+filepath) works
// (ok, nice to memmap them, and later be sorry in the app...)
// So PApplet.createInputRaw(), which most
// loadXYZ() functions use, should work, too.
// but it doesnt? Yep,. tries too much magic.
// In the end, sketchPath() throws up..
// Include the @Override sketchPath() below in your sketch to
// activate a workaround, should work in app and preview

// and if your curious or need to know
///  import android.content.Intent;
boolean isReallyPreview(){
 //// Intent intent = getActivity().getIntent();            
 /// String dataFolder = intent.getStringExtra("SKETCH_DATA_FOLDER");
 /// no ntk for Intent
 String dataFolder = 
    getActivity()
      .getIntent()
      .getStringExtra("SKETCH_DATA_FOLDER");

   // println("data :"+dataFolder);
  // if so, we have a local temporary(!) copy
  // of all assets in data/* already
  // use transparent or like solo(han)
  // if returning false, an app
  // might want to copy files to be mapped
  // (or otherwise not channeled through 
  // PApplet.createInputRaw()) to internal
  // 
  return dataFolder!=null; // or empty..
}

/* testing */
/* off * /
boolean isPreview =false;
boolean testedPreview=false;

// results depend on @Override sketchPass
@Deprecated
boolean isPreview() {
  final String exist="I/i.exist";
  if(testedPreview)
  return isPreview;
  else
  testedPreview=true;
  try {
    if (loadStrings( exist )!=null) {
      flash("App(like), found "+exist+" in assets.");
    } else
      flash("Oops, loadStrings() returned null?");
  }
  catch(Exception e) {
    // flash("Preview");
    isPreview=true; // or there was no i.exist
    // but see whether calsign signed this.
    try {
      //println(sketchPath(exist)); // kaboom...not even an exception?
      if (loadStrings( sketchPath("") +"/"+exist)!=null) {
        flash("Preview, found in local storage");
      } else
        flash("Ahbah..never happens?");
    }
    catch(Exception ee)
    {
      toast("No "+exist+" in assets or "+sketchPath("")+"/"+exist+
      "\nDid you put it in data/"+exist+"?");
    }
  }
  return isPreview;
}
/* ----- */

// sketchPath() is the real culprit.
// wont mess with createInputRaw hodgepodge
// ..for now.

@Override public String sketchPath(String where) {
  // just asking, not creating or checking permissions or existence...
  // ... by using File dependencies with unknown side effects.
  if (sketchPath != null&&where.length()>1)
  {
    // println("sp: "+where+" "+sketchPath); 
    if ('/'==where.charAt(0))
      return where;
    return sketchPath+"/"+where;
  }
  // nonsense, bwtf...
  return super.sketchPath(where);
  /* which is (stinking dead code...) this:
    // isAbsolute() could throw an access exception, but so will writing
   // to the local disk using the sketch path, so this is safe here.
   // for 0120, added a try/catch anyways.
   try {
   if (new File(where).isAbsolute()) return where;
   } catch (Exception e) { }

   return surface.getFileStreamPath(where).getAbsolutePath();

   */
}

// PApplet.createFont() is insisting on loading from assets
// uncool if in APDEpreview (where it was moved to local storage)
// or when trying to load from a specified path (eg. external)
// todo: how about webref?? test it..
import android.graphics.Typeface;
import processing.core.PFont; // just to mention

@Override public PFont createFont(String name, float size) {
  // if we want to load from a file, no
  // point in forceful redirecting to an asset or system font!
  // just do it...and forget xxxAbsolutexx unless it is Vodka
  //println("cf: "+name);
  if (name.charAt(0)=='/'){     
    Typeface baseFont = Typeface.createFromFile(name);
    return new PFont(baseFont, round(size), true, null);
  }
  // so far...but this is not using createInputRaw resolution,
  // for preview..is it worth a fix?
  try{
    // While i am at it.. will also find fonts previously installed locally
    // ?? its a feature ...better explicit for nonoverridden sketchPath
    // call ourself
    PFont loco=createFont(sketchPath("")+"/"+name,size);
    if(loco!=null) return loco;
  } catch ( Exception ignore ){
    // dhoild be more specific
  }
  // native or asset
  return super.createFont(name, size);
  // you dont want to know what that is. really.
}

Tnx for your brillant work.

robotdognz commented 3 years ago

This was incredibly useful, thank you