/* Copyright (c) 2002-2011 by XMLVM.org
*
* Project Info: http://www.xmlvm.org
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package android.app;
import java.lang.ref.WeakReference;
import org.xmlvm.acl.common.subsystems.CommonProperties;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.internal.Assert;
import android.internal.CommonDeviceAPIFinder;
import android.internal.TopActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.ViewGroup.LayoutParams;
/**
* IPhone implementation of Android's Activity class.
*
* @see http://developer.android.com/reference/android/app/Activity.html}
*/
public class Activity extends ContextThemeWrapper {
public static final int RESULT_CANCELED = 0;
public static final int RESULT_OK = -1;
/*
* States according to {@link
* http://developer.motorola.com/docstools/library
* /Android_Applications_for_Java_ME_Developers/}
*/
private static final int STATE_UNINITIALIZED = 0;
private static final int STATE_ACTIVE = 1;
private static final int STATE_PAUSED = 2;
private static final int STATE_STOPPED = 3;
private static final int STATE_DESTROYED = 4;
private int state = STATE_UNINITIALIZED;
private WeakReference<Activity> parent;
private Activity child;
private Intent intent;
private ComponentName componentName;
private int requestCode;
private int resultCode;
private Intent resultData;
private Window window;
private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
private boolean finishing = false;
private boolean shouldGoVisible = true;
private boolean shouldRestartParent = true;
private Handler handler = new Handler();
public void xmlvmSetParent(Activity parent) {
this.parent = new WeakReference<Activity>(parent);
parent.child = this;
}
public final Activity getParent() {
return this.parent == null ? null : this.parent.get();
}
public void xmlvmSetRequestCode(int requestCode) {
this.requestCode = requestCode;
}
public void xmlvmSetIntent(Intent intent) {
this.intent = intent;
}
public void xmlvmSetComponentName(ComponentName componentName) {
this.componentName = componentName;
}
/**
* There are certain situations where an Activity going through onPause()
* should not restart its parent Activity. One situation is where this
* Activity created a new child Activity (in this case, the child should get
* started instead of the parent being restarted). Another situation is when
* the application is pushed to the background where the parent should not
* be restarted.
*/
public void xmlvmShouldRestartParent(boolean flag) {
this.shouldRestartParent = flag;
}
public void xmlvmCreate(final Bundle savedInstanceState) {
Activity parent = getParent();
if (parent != null) {
parent.shouldRestartParent = false;
}
if (parent != null && parent.state == STATE_UNINITIALIZED) {
// We set a flag in the parent to signal that it is about to
// create a child activity. This information is used to prevent
// the parent from becoming visible even though it might enter
// the visible lifecycle (since it will immediately be covered
// again by the new child activity)
parent.shouldGoVisible = false;
}
handler.post(new Runnable() {
public void run() {
create(savedInstanceState);
}
});
}
private void create(Object savedInstanceState) {
Activity prev = TopActivity.get();
if (prev != null && prev.state == STATE_ACTIVE) {
prev.pause();
}
TopActivity.set(Activity.this);
window = new Window(Activity.this);
setRequestedOrientation(screenOrientation);
setResult(Activity.RESULT_CANCELED);
onContentChanged();
onCreate((Bundle) savedInstanceState);
start();
if (TopActivity.get() == Activity.this) {
resume();
}
}
public void xmlvmDestroy() {
handler.post(new Runnable() {
public void run() {
destroy(null);
}
});
}
private void destroy(Object unused) {
if (state == STATE_DESTROYED) {
return;
}
stop();
onDestroy();
if (window != null) {
window.xmlvmRemoveWindow();
window = null;
}
Activity theParent = getParent();
if (child != null) {
child.parent = null;
if (theParent != null) {
child.parent = new WeakReference<Activity>(theParent);
theParent.child = child;
}
} else {
if (theParent != null) {
theParent.child = null;
}
}
child = null;
if (TopActivity.get() == Activity.this) {
TopActivity.set(null);
}
if (theParent != null) {
if (TopActivity.get() == null) {
TopActivity.set(theParent);
}
theParent.onActivityResult(requestCode, resultCode, resultData);
}
parent = null;
state = STATE_DESTROYED;
}
public void xmlvmRestart() {
handler.post(new Runnable() {
public void run() {
restart(null);
}
});
}
private void restart(Object unused) {
onRestart();
resume();
}
public void xmlvmStop() {
handler.post(new Runnable() {
public void run() {
stop();
}
});
}
private void start() {
onStart();
state = STATE_PAUSED;
}
private void resume() {
if (state == STATE_ACTIVE) {
return;
}
if (state == STATE_STOPPED) {
start();
}
onResume();
if (this.shouldGoVisible) {
window.xmlvmSetHidden(false);
}
this.shouldGoVisible = true;
state = STATE_ACTIVE;
Activity parent = getParent();
if (parent != null && parent.state != STATE_STOPPED) {
parent.stop();
}
}
private void pause() {
if (state == STATE_PAUSED) {
return;
}
onPause();
state = STATE_PAUSED;
Activity parent = getParent();
if (parent != null && parent.state != STATE_ACTIVE && this.shouldRestartParent) {
// Only restart the parent if the parent is not active and it
// should be restarted (see xmlvmShouldRestartParent())
parent.restart(null);
}
this.shouldRestartParent = true;
}
private void stop() {
if (state == STATE_STOPPED) {
return;
}
if (state == STATE_ACTIVE) {
pause();
}
// We don't make the window invisible because iOS will capture
// the current content of the screen for its multitasking
// menu
// window.xmlvmSetHidden(true);
onStop();
state = STATE_STOPPED;
}
/**
*
* @param savedInstanceState
* unused as of now
*/
protected void onCreate(Bundle savedInstanceState) {
}
protected void onStart() {
}
protected void onRestart() {
}
protected void onResume() {
}
protected void onPause() {
}
protected void onStop() {
}
/**
* Perform any final cleanup before an activity is destroyed. Can be
* overridden by subclasses.
*/
protected void onDestroy() {
}
/**
*
* @param requestCode
* @param resultCode
* unused as of now
* @param data
*/
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
}
protected void onSaveInstanceState(Bundle bundle) {
}
public void onBackPressed() {
}
public void setContentView(View view) {
window.setContentView(view);
if (state == STATE_ACTIVE) {
window.xmlvmSetHidden(false);
}
}
public void onLowMemory() {
// Should be overridden in derived classes to release unneeded resources
}
public void setContentView(int id) {
window.setContentView(id);
if (state == STATE_ACTIVE) {
window.xmlvmSetHidden(false);
}
}
public void addContentView(View view, LayoutParams params) {
window.addContentView(view, params);
}
public View findViewById(int id) {
return window.findViewById(id);
}
/**
* Unimplemented on iPhone
*
* @param streamType
* unused
*/
public final void setVolumeControlStream(int streamType) {
// There's nothing appropriate on the iPhone so this implementation
// will remain empty
}
/**
* TODO: Implement for real.
*/
public WindowManager getWindowManager() {
return new WindowManager();
}
/**
* Can be overridden by subclasses.
*/
public void onContentChanged() {
}
public void showDialog(int id) {
Dialog dialog = onCreateDialog(id);
if (dialog != null)
dialog.show();
}
protected Dialog onCreateDialog(int id) {
return null;
}
/**
* Can be overridden by subclasses that want to create a menu.
*/
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
/**
* Can be overridden by subclasses that want to handle menu button presses.
*/
public boolean onOptionsItemSelected(MenuItem item) {
return true;
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
return false;
}
public final void setResult(int resultCode) {
setResult(resultCode, null);
}
public final void setResult(int resultCode, Intent data) {
this.resultCode = resultCode;
this.resultData = data;
}
public void requestWindowFeature(int feature) {
switch (feature) {
case Window.FEATURE_NO_TITLE:
// TODO: This will remove the title
// bar, but not get rid of the status bar. On the iPhone we don't
// have a title, but maybe we should implement this as a
// compatibility feature. Once we have it, this is the place to
// disable it.
break;
default:
Assert.NOT_IMPLEMENTED();
}
}
/**
* Retrieve the current {@link Window} for the activity.
*/
public Window getWindow() {
return window;
}
/**
* Change the desired orientation of this activity.
*/
public void setRequestedOrientation(int requestedOrientation) {
screenOrientation = requestedOrientation;
switch (requestedOrientation) {
case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
CommonDeviceAPIFinder.instance().getProperties()
.setOrientation(CommonProperties.ORIENTATION_LANDSCAPE_LEFT);
break;
case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
CommonDeviceAPIFinder.instance().getProperties()
.setOrientation(CommonProperties.ORIENTATION_PORTRAIT);
break;
}
}
public int getRequestedOrientation() {
return screenOrientation;
}
/**
* Retrieve a {@link SharedPreferences} object for accessing preferences
* that are private to this activity.
*
* @param mode
* Operating mode. Use {@link Context#MODE_PRIVATE} for the
* default operation, {@link Context#MODE_WORLD_READABLE} and
* {@link Context#MODE_WORLD_WRITEABLE} to control permissions.
*
* @return Returns the single SharedPreferences instance that can be used to
* retrieve and modify the preference values.
*/
public SharedPreferences getPreferences(int mode) {
return getSharedPreferences(this.getClass().getName(), mode);
}
/**
* @return
*/
protected Intent getIntent() {
return intent;
}
public ComponentName getComponentName() {
return componentName;
}
public LayoutInflater getLayoutInflater() {
return (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
}
public void finish() {
finishing = true;
xmlvmDestroy();
finishing = false;
}
public boolean isFinishing() {
return finishing;
}
public void onConfigurationChanged(Configuration newConfig) {
Assert.NOT_IMPLEMENTED();
}
public boolean onMenuItemSelected(int featureId, MenuItem item) {
Assert.NOT_IMPLEMENTED();
return false;
}
public MenuInflater getMenuInflater() {
Assert.NOT_IMPLEMENTED();
return null;
}
public void setTitle(CharSequence title) {
Assert.NOT_IMPLEMENTED();
}
@Override
public String getString(int id) {
return this.getResources().getText(id);
}
public final void runOnUiThread(Runnable action) {
handler.post(action);
}
public String getLocalClassName() {
return componentName.getClassName();
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
return false;
}
}