/**
* Copyright (c) 2013, Redsolution LTD. All rights reserved.
*
* This file is part of Xabber project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License, Version 3.
*
* Xabber 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 com.xabber.android.data;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import com.xabber.android.R;
import com.xabber.android.data.connection.CertificateManager;
import com.xabber.android.data.log.LogManager;
import com.xabber.android.ui.activity.AboutActivity;
import com.xabber.android.ui.activity.ContactListActivity;
import com.xabber.android.ui.activity.IntroActivity;
import com.xabber.android.ui.activity.LoadActivity;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.WeakHashMap;
/**
* Activity stack manager.
*
* @author alexander.ivanov
*/
public class ActivityManager implements OnUnloadListener {
private static final String EXTRA_TASK_INDEX = "com.xabber.android.data.ActivityManager.EXTRA_TASK_INDEX";
private static final boolean LOG = true;
private static ActivityManager instance;
private final Application application;
/**
* List of launched activities.
*/
private final ArrayList<Activity> activities;
/**
* Activity with index of it task.
*/
private final WeakHashMap<Activity, Integer> taskIndexes;
/**
* Next index of task.
*/
private int nextTaskIndex;
/**
* Listener for errors.
*/
private OnErrorListener onErrorListener;
public static ActivityManager getInstance() {
if (instance == null) {
instance = new ActivityManager();
}
return instance;
}
private ActivityManager() {
this.application = Application.getInstance();
activities = new ArrayList<>();
nextTaskIndex = 0;
taskIndexes = new WeakHashMap<>();
}
/**
* Removes finished activities from stask.
*/
private void rebuildStack() {
Iterator<Activity> iterator = activities.iterator();
while (iterator.hasNext()) {
if (iterator.next().isFinishing()) {
iterator.remove();
}
}
}
/**
* Finish all activities in stack till the root contact list.
*
* @param finishRoot also finish root contact list.
*/
public void clearStack(boolean finishRoot) {
ContactListActivity root = null;
rebuildStack();
for (Activity activity : activities) {
if (!finishRoot && root == null && activity instanceof ContactListActivity) {
root = (ContactListActivity) activity;
} else {
activity.finish();
}
}
rebuildStack();
}
/**
* @return Whether contact list is in the activity stack.
*/
public boolean hasContactList(Context context) {
rebuildStack();
for (Activity activity : activities)
if (activity instanceof ContactListActivity)
return true;
return false;
}
/**
* Apply theme settings.
*
* @param activity
*/
private void applyTheme(Activity activity) {
activity.setTheme(R.style.Theme);
SettingsManager.InterfaceTheme theme = SettingsManager.interfaceTheme();
if (theme.equals(SettingsManager.InterfaceTheme.dark)) {
activity.setTheme(R.style.ThemeDark);
} else {
activity.setTheme(R.style.Theme);
}
}
/**
* Push activity to stack.
* <p/>
* Must be called from {@link Activity#onCreate(Bundle)}.
*
* @param activity
*/
public void onCreate(Activity activity) {
if (LOG) {
LogManager.i(activity, "onCreate: " + activity.getIntent());
}
if (!(activity instanceof AboutActivity) && !(activity instanceof IntroActivity)) {
applyTheme(activity);
}
if (application.isClosing() && !(activity instanceof LoadActivity)) {
activity.startActivity(LoadActivity.createIntent(activity));
activity.finish();
}
activities.add(activity);
rebuildStack();
fetchTaskIndex(activity, activity.getIntent());
}
/**
* Pop activity from stack.
* <p/>
* Must be called from {@link Activity#onDestroy()}.
*
* @param activity
*/
public void onDestroy(Activity activity) {
if (LOG)
LogManager.i(activity, "onDestroy");
activities.remove(activity);
}
/**
* Pause activity.
* <p/>
* Must be called from {@link Activity#onPause()}.
*
* @param activity
*/
public void onPause(Activity activity) {
if (LOG)
LogManager.i(activity, "onPause");
CertificateManager.getInstance().unregisterActivity(activity);
if (onErrorListener != null)
application
.removeUIListener(OnErrorListener.class, onErrorListener);
onErrorListener = null;
}
/**
* Resume activity.
* <p/>
* Must be called from {@link Activity#onResume()}.
*
* @param activity
*/
public void onResume(final Activity activity) {
if (LOG) {
LogManager.i(activity, "onResume");
}
if (!application.isInitialized() && !(activity instanceof LoadActivity)) {
if (LOG) {
LogManager.i(this, "Wait for loading");
}
activity.startActivity(LoadActivity.createIntent(activity));
}
if (onErrorListener != null) {
application.removeUIListener(OnErrorListener.class, onErrorListener);
}
onErrorListener = new OnErrorListener() {
@Override
public void onError(final int resourceId) {
Toast.makeText(activity, activity.getString(resourceId),
Toast.LENGTH_LONG).show();
}
};
application.addUIListener(OnErrorListener.class, onErrorListener);
CertificateManager.getInstance().registerActivity(activity);
}
/**
* New intent received.
* <p/>
* Must be called from {@link Activity#onNewIntent(Intent)}.
*
* @param activity
* @param intent
*/
public void onNewIntent(Activity activity, Intent intent) {
if (LOG)
LogManager.i(activity, "onNewIntent: " + intent);
}
/**
* Result has been received.
* <p/>
* Must be called from {@link Activity#onActivityResult(int, int, Intent)}.
*
* @param activity
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(Activity activity, int requestCode,
int resultCode, Intent data) {
if (LOG)
LogManager.i(activity, "onActivityResult: " + requestCode + ", "
+ resultCode + ", " + data);
}
/**
* Adds task index to the intent if specified for the source activity.
* <p/>
* Must be used when source activity starts new own activity from
* {@link Activity#startActivity(Intent)} and
* {@link Activity#startActivityForResult(Intent, int)}.
*
* @param source
* @param intent
*/
public void updateIntent(Activity source, Intent intent) {
Integer index = taskIndexes.get(source);
if (index == null)
return;
intent.putExtra(EXTRA_TASK_INDEX, index);
}
/**
* Mark activity to be in separate activity stack.
*
* @param activity
*/
public void startNewTask(Activity activity) {
taskIndexes.put(activity, nextTaskIndex);
LogManager.i(activity, "Start new task " + nextTaskIndex);
nextTaskIndex += 1;
}
/**
* Either move main task to back, either close all activities in subtask.
*
* @param activity
*/
public void cancelTask(Activity activity) {
Integer index = taskIndexes.get(activity);
LogManager.i(activity, "Cancel task " + index);
if (index == null) {
activity.moveTaskToBack(true);
} else {
for (Entry<Activity, Integer> entry : taskIndexes.entrySet()) {
if (entry.getValue().equals(index)) {
entry.getKey().finish();
}
}
}
}
/**
* Fetch task index from the intent and mark specified activity.
*
* @param activity
* @param intent
*/
private void fetchTaskIndex(Activity activity, Intent intent) {
int index = intent.getIntExtra(EXTRA_TASK_INDEX, -1);
if (index == -1)
return;
LogManager.i(activity, "Fetch task index " + index);
taskIndexes.put(activity, index);
}
@Override
public void onUnload() {
Application.getInstance().runOnUiThread(new Runnable() {
@Override
public void run() {
clearStack(true);
}
});
}
}