Skip to content

Commit

Permalink
* Issue #395 Display initial progress dialog while activity script is…
Browse files Browse the repository at this point in the history
… loading.
  • Loading branch information
donv committed Apr 17, 2013
1 parent 902e14d commit cbe9286
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 87 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
ruboto (0.11.0)
ruboto (0.12.0.dev)
main (>= 4.7.2)

GEM
Expand Down
2 changes: 1 addition & 1 deletion assets/samples/sample_broadcast_receiver.rb
@@ -1,7 +1,7 @@
import android.util.Log

class SampleBroadcastReceiver
# will get called whenever the BroadcastReceiver receives an intent (whenever onReceive is called)
# Will get called whenever the BroadcastReceiver receives an intent.
def onReceive(context, intent)
Log.v 'SampleBroadcastReceiver', 'Broadcast received!'
Log.v 'SampleBroadcastReceiver', intent.getExtras.to_s
Expand Down
5 changes: 4 additions & 1 deletion assets/src/RubotoActivity.java
Expand Up @@ -48,15 +48,18 @@ public void onCreate(Bundle bundle) {
}

if (JRubyAdapter.isInitialized() && scriptInfo.isReadyToLoad()) {
ScriptLoader.loadScript(this, (Object[]) args);
ScriptLoader.loadScript(this);
ScriptLoader.callOnCreate(this, (Object[]) args);
} else {
super.onCreate(bundle);
}
}

// FIXME(uwe): What is this for?
public boolean rubotoAttachable() {
return true;
}
// EMXIF

/****************************************************************************************
*
Expand Down
5 changes: 4 additions & 1 deletion assets/src/RubotoBroadcastReceiver.java
Expand Up @@ -30,7 +30,7 @@ public THE_RUBOTO_CLASS(String name) {

public void onReceive(android.content.Context context, android.content.Intent intent) {
try {
Log.d("onReceive: " + this);
Log.d("onReceive: " + this + " " + ScriptLoader.isCalledFromJRuby() + " " + scriptLoaded);
if (ScriptLoader.isCalledFromJRuby()) {
return;
}
Expand All @@ -43,6 +43,8 @@ public void onReceive(android.content.Context context, android.content.Intent in
}
}

Log.d("onReceive: JRuby version: " + JRubyAdapter.isJRubyPreOneSeven() + " " + JRubyAdapter.isJRubyOneSeven() + " " + JRubyAdapter.runScriptlet(scriptInfo.getRubyClassName() + ".instance_methods(false).any?{|m| m.to_sym == :onReceive}") + " " + scriptInfo.getRubyClassName());
Log.d("onReceive " + this + " " + scriptInfo.getRubyInstance() + "(" + scriptInfo.getRubyClassName() + "): " + JRubyAdapter.runScriptlet(scriptInfo.getRubyClassName() + ".instance_methods(false)"));
// FIXME(uwe): Simplify when we stop supporting JRuby 1.6.x
if (JRubyAdapter.isJRubyPreOneSeven()) {
JRubyAdapter.put("$broadcast_receiver", this);
Expand All @@ -52,6 +54,7 @@ public void onReceive(android.content.Context context, android.content.Intent in
} else if (JRubyAdapter.isJRubyOneSeven()) {
// FIXME(uwe): Simplify when we stop support for snake case aliasing interface callback methods.
if ((Boolean)JRubyAdapter.runScriptlet(scriptInfo.getRubyClassName() + ".instance_methods(false).any?{|m| m.to_sym == :onReceive}")) {
Log.d("onReceive: call method");
JRubyAdapter.runRubyMethod(this, "onReceive", new Object[]{context, intent});
} else if ((Boolean)JRubyAdapter.runScriptlet(scriptInfo.getRubyClassName() + ".instance_methods(false).any?{|m| m.to_sym == :on_receive}")) {
JRubyAdapter.runRubyMethod(this, "on_receive", new Object[]{context, intent});
Expand Down
72 changes: 42 additions & 30 deletions assets/src/org/ruboto/EntryPointActivity.java
Expand Up @@ -13,6 +13,13 @@
import android.widget.TextView;
import android.widget.Toast;

/**
* This Activity acts as an entry point to the app. It must initialize the
* JRuby runtime before continuing its life cycle.
* While JRuby is initializing, a progress dialog is shown.
* If R.layout.splash is defined, by adding a res/layout/splash.xml file,
* this layout is displayed instead of the progress dialog.
*/
public class EntryPointActivity extends org.ruboto.RubotoActivity {
private int splash = 0;
private ProgressDialog loadingDialog;
Expand All @@ -24,7 +31,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
// EMXIF

public void onCreate(Bundle bundle) {
Log.d("onCreate: ");
Log.d("EntryPointActivity onCreate:");

try {
splash = Class.forName(getPackageName() + ".R$layout").getField("splash").getInt(null);
Expand All @@ -34,6 +41,9 @@ public void onCreate(Bundle bundle) {

if (JRubyAdapter.isInitialized()) {
appStarted = true;
} else {
showProgress();
initJRuby(true);
}
super.onCreate(bundle);
}
Expand All @@ -53,26 +63,6 @@ public void onResume() {
fireRubotoActivity();
} else {
Log.d("Not initialized");
showProgress();
receiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent) {
Log.i("received broadcast: " + intent);
Log.i("URI: " + intent.getData());
if (intent.getData().toString().equals("package:org.ruboto.core")) {
Toast.makeText(context,"Ruboto Core is now installed.",Toast.LENGTH_SHORT).show();
if (receiver != null) {
unregisterReceiver(receiver);
receiver = null;
}
showProgress();
initJRuby(false);
}
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package");
registerReceiver(receiver, filter);
initJRuby(true);
super.onResume();
}
}
Expand Down Expand Up @@ -103,12 +93,9 @@ public void run() {
final boolean jrubyOk = JRubyAdapter.setUpJRuby(EntryPointActivity.this);
if (jrubyOk) {
Log.d("onResume: JRuby OK");
runOnUiThread(new Runnable() {
public void run() {
fireRubotoActivity();
}
});
fireRubotoActivity();
} else {
registerPackageInstallReceiver();
runOnUiThread(new Runnable() {
public void run() {
if (firstTime) {
Expand Down Expand Up @@ -152,10 +139,15 @@ protected void fireRubotoActivity() {
if(appStarted) return;
appStarted = true;
Log.i("Starting activity");
ScriptLoader.loadScript(this, args[0]);
onStart();
super.onResume();
hideProgress();
ScriptLoader.loadScript(this);
runOnUiThread(new Runnable() {
public void run() {
ScriptLoader.callOnCreate(EntryPointActivity.this, args[0]);
onStart();
onResume();
hideProgress();
}
});
}

private void showProgress() {
Expand Down Expand Up @@ -186,4 +178,24 @@ private void hideProgress() {
}
}

private void registerPackageInstallReceiver() {
receiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent) {
Log.i("received broadcast: " + intent);
Log.i("URI: " + intent.getData());
if (intent.getData().toString().equals("package:org.ruboto.core")) {
Toast.makeText(context,"Ruboto Core is now installed.",Toast.LENGTH_SHORT).show();
if (receiver != null) {
unregisterReceiver(receiver);
receiver = null;
}
showProgress();
initJRuby(false);
}
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package");
registerReceiver(receiver, filter);
}
}
70 changes: 36 additions & 34 deletions assets/src/org/ruboto/ScriptLoader.java
Expand Up @@ -21,15 +21,17 @@ public static boolean isCalledFromJRuby() {
return false;
}

public static void loadScript(final RubotoComponent component, Object... args) {
public static void loadScript(final RubotoComponent component) {
try {
if (component.getScriptInfo().getScriptName() != null) {
System.out.println("Looking for Ruby class: " + component.getScriptInfo().getRubyClassName());
Log.d("Loading component: " + component);
Log.d("Looking for Ruby class: " + component.getScriptInfo().getRubyClassName());
Object rubyClass = JRubyAdapter.get(component.getScriptInfo().getRubyClassName());
System.out.println("Found: " + rubyClass);
Log.d("Found: " + rubyClass);
final Script rubyScript = new Script(component.getScriptInfo().getScriptName());
Object rubyInstance;
if (rubyScript.exists()) {
Log.d("Found script.");
rubyInstance = component;
final String script = rubyScript.getContents();
boolean scriptContainsClass = script.matches("(?s).*class "
Expand All @@ -38,12 +40,13 @@ public static void loadScript(final RubotoComponent component, Object... args) {
.equals(component.getClass().getSimpleName());
if (scriptContainsClass) {
if (hasBackingJavaClass) {
Log.d("hasBackingJavaClass");
if (rubyClass != null && !rubyClass.toString().startsWith("Java::")) {
System.out.println("Found Ruby class instead of Java class. Reloading.");
Log.d("Found Ruby class instead of Java class. Reloading.");
rubyClass = null;
}
} else {
System.out.println("Script defines methods on meta class");
Log.d("Script defines methods on meta class");

// FIXME(uwe): Simplify when we stop support for RubotoCore 0.4.7
if (JRubyAdapter.isJRubyPreOneSeven() || JRubyAdapter.isRubyOneEight()) {
Expand All @@ -60,11 +63,11 @@ public static void loadScript(final RubotoComponent component, Object... args) {
}
}
if (rubyClass == null || !hasBackingJavaClass) {
System.out.println("Loading script: " + component.getScriptInfo().getScriptName());
Log.d("Loading script: " + component.getScriptInfo().getScriptName());
if (scriptContainsClass) {
System.out.println("Script contains class definition");
Log.d("Script contains class definition");
if (rubyClass == null && hasBackingJavaClass) {
System.out.println("Script has separate Java class");
Log.d("Script has separate Java class");

// FIXME(uwe): Simplify when we stop support for JRuby < 1.7.0
if (!JRubyAdapter.isJRubyPreOneSeven()) {
Expand All @@ -74,15 +77,15 @@ public static void loadScript(final RubotoComponent component, Object... args) {

rubyClass = JRubyAdapter.runScriptlet("Java::" + component.getClass().getName());
}
System.out.println("Set class: " + rubyClass);
Log.d("Set class: " + rubyClass);
JRubyAdapter.put(component.getScriptInfo().getRubyClassName(), rubyClass);
// FIXME(uwe): Collect these threads in a ThreadGroup ?
Thread t = new Thread(null, new Runnable(){
public void run() {
long loadStart = System.currentTimeMillis();
JRubyAdapter.setScriptFilename(rubyScript.getAbsolutePath());
JRubyAdapter.runScriptlet(script);
System.out.println("Script load took " + (System.currentTimeMillis() - loadStart) + "ms");
Log.d("Script load took " + (System.currentTimeMillis() - loadStart) + "ms");
}
}, "ScriptLoader for " + rubyClass, 128 * 1024);
try {
Expand All @@ -101,19 +104,16 @@ public void run() {
}
} else if (rubyClass != null) {
// We have a predefined Ruby class without corresponding Ruby source file.
System.out.println("Create separate Ruby instance for class: " + rubyClass);
Log.d("Create separate Ruby instance for class: " + rubyClass);
rubyInstance = JRubyAdapter.runRubyMethod(rubyClass, "new");
JRubyAdapter.runRubyMethod(rubyInstance, "instance_variable_set", "@ruboto_java_instance", component);
} else {
// Neither script file nor predefined class
Log.e("Missing script and class. Either script or predefined class must be present.");
throw new RuntimeException("Either script or predefined class must be present.");
}
if (rubyClass != null) {
if (component instanceof android.content.Context) {
callOnCreate(rubyInstance, args, component.getScriptInfo().getRubyClassName());
}
}
component.getScriptInfo().setRubyInstance(rubyInstance);
Log.d(component.getScriptInfo().getRubyInstance() + "(" + component.getScriptInfo().getRubyClassName() + "): " + JRubyAdapter.runScriptlet(component.getScriptInfo().getRubyClassName() + ".instance_methods(false)"));
}
} catch(IOException e){
e.printStackTrace();
Expand All @@ -123,27 +123,29 @@ public void run() {
}
}

private static final void callOnCreate(Object rubyInstance, Object[] args, String rubyClassName) {
System.out.println("Call onCreate on: " + rubyInstance + ", " + JRubyAdapter.get("JRUBY_VERSION"));
// FIXME(uwe): Simplify when we stop support for RubotoCore 0.4.7
if (JRubyAdapter.isJRubyPreOneSeven()) {
if (args.length > 0) {
JRubyAdapter.put("$bundle", args[0]);
}
JRubyAdapter.put("$ruby_instance", rubyInstance);
JRubyAdapter.runScriptlet("$ruby_instance.on_create(" + (args.length > 0 ? "$bundle" : "") + ")");
} else if (JRubyAdapter.isJRubyOneSeven()) {
// FIXME(uwe): Simplify when we stop support for snake case aliasing interface callback methods.
if ((Boolean)JRubyAdapter.runScriptlet(rubyClassName + ".instance_methods(false).any?{|m| m.to_sym == :onCreate}")) {
JRubyAdapter.runRubyMethod(rubyInstance, "onCreate", args);
} else if ((Boolean)JRubyAdapter.runScriptlet(rubyClassName + ".instance_methods(false).any?{|m| m.to_sym == :on_create}")) {
JRubyAdapter.runRubyMethod(rubyInstance, "on_create", args);
public static final void callOnCreate(final RubotoComponent component, Object... args) {
if (component instanceof android.content.Context) {
Log.d("Call onCreate on: " + component.getScriptInfo().getRubyInstance() + ", " + JRubyAdapter.get("JRUBY_VERSION"));
// FIXME(uwe): Simplify when we stop support for RubotoCore 0.4.7
if (JRubyAdapter.isJRubyPreOneSeven()) {
if (args.length > 0) {
JRubyAdapter.put("$bundle", args[0]);
}
JRubyAdapter.put("$ruby_instance", component.getScriptInfo().getRubyInstance());
JRubyAdapter.runScriptlet("$ruby_instance.on_create(" + (args.length > 0 ? "$bundle" : "") + ")");
} else if (JRubyAdapter.isJRubyOneSeven()) {
// FIXME(uwe): Simplify when we stop support for snake case aliasing interface callback methods.
if ((Boolean)JRubyAdapter.runScriptlet(component.getScriptInfo().getRubyClassName() + ".instance_methods(false).any?{|m| m.to_sym == :onCreate}")) {
JRubyAdapter.runRubyMethod(component.getScriptInfo().getRubyInstance(), "onCreate", args);
} else if ((Boolean)JRubyAdapter.runScriptlet(component.getScriptInfo().getRubyClassName() + ".instance_methods(false).any?{|m| m.to_sym == :on_create}")) {
JRubyAdapter.runRubyMethod(component.getScriptInfo().getRubyInstance(), "on_create", args);
}
// EMXIF
} else {
throw new RuntimeException("Unknown JRuby version: " + JRubyAdapter.get("JRUBY_VERSION"));
}
// EMXIF
} else {
throw new RuntimeException("Unknown JRuby version: " + JRubyAdapter.get("JRUBY_VERSION"));
}
// EMXIF
}

}
File renamed without changes.
41 changes: 41 additions & 0 deletions doc/startup.txt
@@ -0,0 +1,41 @@
Different startup sequences

Initial entry point activity:

InheritedActivity EntryPointActivity RubotoActivity Activity
=================================================================================
onCreate
scriptInfo.setClassName ===> onCreate
showProgress
initJRuby ===> onCreate ===> onCreate

===========================> onResume ===> onResume ===> onResume

===========================> fireRubotoActivity
ScriptLoader.loadScript
runOnUiThread:
ScriptLoader.callOnCreate
onStart ===> onStart ===> onStart Duplicate!
onResume ===> onResume ===> onResume Duplicate!
hideProgress

Second entry point activity:

InheritedActivity EntryPointActivity RubotoActivity Activity
=================================================================================
onCreate
scriptInfo.setClassName
setClassName ===> onCreate
appStarted = true ===> onCreate
ScriptLoader.loadScript
ScriptLoader.callOnCreate => onCreate

===========================> onResume ===> onResume ===> onResume



RubotoActivity with class name:

RubotoActivity Activity
=================================================================================
onCreate

0 comments on commit cbe9286

Please sign in to comment.