Skip to content

Latest commit

 

History

History
202 lines (173 loc) · 6.84 KB

09-Ti.AppLifeCycle.md

File metadata and controls

202 lines (173 loc) · 6.84 KB

#Ti.AppLifecycle

##foreGround/backGround An android app is a container of activities and other components. These activities are the visible screens, with other words the UI for the user. They lifes in lifecycles.

If we have a reference to an activity then we all events are available, in Titanium too.

###Problem In some cases we need an info about state on app level. The only way is a dirty way to inspect which activity in foreground. If the activity is changed then we can fire an event.

###Idea In module we start a cronjob. The cronjob looks in process table and detects if the toppest task is changed. The cronJob.scheduleAtFixedRate method is related to setInterval() of javascript. The code inside the brackets is related to to function in javascript. We start the code at onAppCreate method. And we don't forget to cancel the cronjob at onAppDestroy method.

cronJob.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				boolean isInFront = false;
				TaskTestResult result = isInForeground();
				isInFront = result.getIsForground();
				if (isInFront != wasInForeGround
						|| lastPackageName != result.getPackageName()) {
					Log.d(LCAT,
							"new top package name: >>>>>>>>> "
									+ result.getPackageName());
					String key = (isInFront == true) ? "resumed" : "paused";
					if (FIRE_EVENT_ENABLED == true) {
						KrollDict dict = new KrollDict();
						dict.put("packageName", result.getPackageName());
						mApp.fireAppEvent(key, dict);
					}
					Log.d(LCAT, key);
					wasInForeGround = isInFront;
				}
			}
		}, 0, testInterval);

The method isInForeground make a new instance of a class ForegroundCheckTask.

static public TaskTestResult isInForeground() {
		try {
			TaskTestResult result = new ForegroundCheckTask().execute(
					TiApplication.getInstance().getApplicationContext()).get();
			return result;
		} catch (InterruptedException e) {
			e.printStackTrace();
			return null;
		} catch (ExecutionException e) {
			e.printStackTrace();
			return null;
		}
	}

###Test interval The interval of cronjob is critical: if to short, the bad for CPU and if too long then

  1. delay of event
  2. we cannot fire event because reference is null In tiapp.xml we can use a property for setting:
<property name="LIFECYCLE_TESTINTERVAL" type="int">200</property>

For read this properties we need a reference to it:

private static TiProperties appProperties = TiApplication.getInstance().getAppProperties();

and we can read it by

private static int testInterval = appProperties.getInt("LIFECYCLE_TESTINTERVAL", 1000);

The class needs two result values:

  1. a boolean for info if foreground or not
  2. a string with top packageName

In PHP or SWIFT we can use tuples for results of functions – in Java we need a custome class:

public final class TaskTestResult {
	private final boolean isForeground;
	private final String packageName;
	public TaskTestResult(boolean isForeground, String packageName) {
		this.isForeground = isForeground;
		this.packageName = packageName;
	}
	public boolean getIsForeground() {
		return isForeground;
	}
	public String getPackageName() {
		return packageName;
	}
}

With other words: we build a variable with special struct and we define getters. The setting happens in constructor (in our case).

Now we can inspect the core:

class ForegroundCheckTask extends AsyncTask<Context, Void, TaskTestResult> {
	@Override
	protected TaskTestResult doInBackground(Context... params) {
		final Context context = params[0];
		return isAppOnForeground(context);
	}

	@SuppressWarnings("deprecation")
	private TaskTestResult isAppOnForeground(Context context) {
		ActivityManager am = (ActivityManager) context
				.getSystemService(Context.ACTIVITY_SERVICE);
		/* retreiving packagename of toppest task */
		String foregroundTaskPackageName = "";
		if (Build.VERSION.SDK_INT > 20) {
			foregroundTaskPackageName = am.getRunningAppProcesses().get(0).processName;
		} else {
			foregroundTaskPackageName = am.getRunningTasks(1).get(0).topActivity
					.getPackageName();
		}
		PackageManager pm = context.getPackageManager();
		PackageInfo foregroundAppPackageInfo = null;
		try {
			foregroundAppPackageInfo = pm.getPackageInfo(
					foregroundTaskPackageName, 0);
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
		String foregroundTaskAppId = foregroundAppPackageInfo.applicationInfo.packageName;
		return new TaskTestResult(context.getPackageName().equals(
				foregroundTaskAppId), foregroundTaskAppId);
	}
}

Depending on Android API level (hell) we need two different api methods:

if (Build.VERSION.SDK_INT > 20) {
            foregroundTaskPackageName = am.getRunningAppProcesses().get(0).processName;
        } else {
            foregroundTaskPackageName = am.getRunningTasks(1).get(0).topActivity
                    .getPackageName();
        }

##Screen On/Off Detecting of screen is simpler because android provide a standard way. Similar to Titaniums global evnt firering aka Ti.App.addEventListener() we have on operating system level the BroadcastReceivers. These can subscribe to system events. the payload is realized with an intent.

We need an intent with a fitting filter:

final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);

Then we can call our custome class wwith this intentFilter:

mReceiver = new ScreenReceiver();
context.registerReceiver(mReceiver, intentFilter);

Here the content of ScreenReceiver:

package de.appwerft.applifecycle;

public class ScreenReceiver extends BroadcastReceiver {
	@Override
	public void onReceive(final Context context, final Intent intent) {
		if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
			de.appwerft.applifecycle.ApplifecycleModule.onScreenChanged(false);

		} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
			de.appwerft.applifecycle.ApplifecycleModule.onScreenChanged(true);
		}
	}
}

"Our" receiver is a child of 'BroadcastReceiver' and overides only one method 'onReceive'. The method has to parameters:

  1. the (unused) context
  2. the intent, the intent contains payload from event

Because we have no scope to app we call two functions in module.

In module we resolve:

public static void onScreenChanged(Boolean screenstate) {
	mApp.fireAppEvent((screenstate == true) ? "screenon" : "screenoff", new KrollDict());
}

and send back to Ti.App. In javascript layer we can:

Ti.App.addEventListener('screenon',function(){
	Ti.UI.createNotification({message:"Welcome back!"}).show();
})

Repo is here