/*
Copyright 2013 The MITRE Corporation, All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this work except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.mitre.svmp.activities;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.ActionBar.*;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.view.*;
import android.widget.GridView;
import org.mitre.svmp.client.R;
import org.mitre.svmp.common.AppInfo;
import org.mitre.svmp.common.ConnectionInfo;
/**
* @author Joe Portner
* This activity displays a list of remote apps that are available for a given Connection
*/
public class AppList extends SvmpActivity {
private static String TAG = AppList.class.getName();
public static final int REQUEST_REFRESHAPPS_QUICK = 200; // send a request for the app list, get result, and finish
public static final int REQUEST_REFRESHAPPS_FULL = 201; // send a request for the app list, get result, and finish
public static final int REQUEST_STARTAPP_RESUME = 202; // resume the activity after returning
public static final int REQUEST_STARTAPP_FINISH = 203; // if we return without errors, finish the activity
protected int connectionID;
protected ConnectionInfo connectionInfo;
protected boolean launchedFromShortcut;
private TabListener<AppListFragment> allApps;
private TabListener<AppListFragment> favorites;
private int sendRequestCode;
private AppInfo sendAppInfo;
public void onCreate(Bundle savedInstanceState) {
// get intent data
Intent intent = getIntent();
connectionID = intent.getIntExtra("connectionID", 0);
super.onCreate(savedInstanceState, -1);
// Notice that setContentView() is not used, because we use the root
// android.R.id.content as the container for each fragment
ActionBar actionBar = getActionBar();
if (actionBar != null) {
// some functions are ICS-only
if (API_14)
onCreateICS(actionBar);
actionBar.setDisplayHomeAsUpEnabled(true);
// set title text
connectionInfo = dbHandler.getConnectionInfo(connectionID);
// setup action bar for tabs
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(false);
allApps = new TabListener<AppListFragment>(
this, "allApps", AppListFragment.class);
Tab tab = actionBar.newTab()
.setText(R.string.appList_actionBar_allApps)
.setTabListener(allApps);
actionBar.addTab(tab);
favorites = new TabListener<AppListFragment>(
this, "favorites", AppListFragment.class);
tab = actionBar.newTab()
.setText(R.string.appList_actionBar_favorites)
.setTabListener(favorites);
actionBar.addTab(tab);
}
// if this was started by a desktop shortcut, just launch the requested app
if(ACTION_LAUNCH_APP.equals(intent.getAction())) {
launchedFromShortcut = true;
String packageName = intent.getStringExtra("packageName");
AppInfo appInfo = dbHandler.getAppInfo(connectionID, packageName);
openApp(appInfo, false); // open the app; if we return without errors, finish the activity
}
}
@TargetApi(14)
private void onCreateICS(ActionBar actionBar) {
// enable the app icon as an Up button
actionBar.setHomeButtonEnabled(true);
}
// called onResume so preference changes take effect in the layout
// repopulates all fragment layouts
@Override
protected void populateLayout() {
allApps.populateLayout();
favorites.populateLayout();
}
// called onResume so preference changes take effect in the layout
@Override
protected void refreshPreferences() {
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.applistmenu, menu);
return super.onCreateOptionsMenu(menu);
}
// this method is called once the menu is selected
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch( menuItem.getItemId() ) {
// the back button displayed as the home icon
case android.R.id.home:
// if we launched this activity directly from a shortcut, start a new ConnectionList activity
if( launchedFromShortcut ) {
Intent intent = new Intent(this, ConnectionList.class);
startActivity(intent);
}
finish();
return true;
case R.id.refreshAppListQuick:
this.sendRequestCode = REQUEST_REFRESHAPPS_QUICK;
authPrompt(connectionInfo); // utilizes "startActivityForResult", which uses this.sendRequestCode
return true;
case R.id.refreshAppListFull:
this.sendRequestCode = REQUEST_REFRESHAPPS_FULL;
authPrompt(connectionInfo); // utilizes "startActivityForResult", which uses this.sendRequestCode
return true;
}
return super.onOptionsItemSelected(menuItem);
}
@Override
protected void afterStartAppRTC(ConnectionInfo connectionInfo) {
// after we have handled the auth prompt and made sure the service is started...
// create explicit intent
Intent intent = new Intent();
if (this.sendRequestCode == REQUEST_REFRESHAPPS_QUICK || this.sendRequestCode == REQUEST_REFRESHAPPS_FULL) {
// we're refreshing our cached list of apps that reside on the VM
intent.setClass(AppList.this, AppRTCRefreshAppsActivity.class);
if (this.sendRequestCode == REQUEST_REFRESHAPPS_FULL)
intent.putExtra("fullRefresh", true);
}
else {
// we're starting the video feed and launching a specific app
intent.setClass(AppList.this, AppRTCVideoActivity.class);
intent.putExtra("pkgName", sendAppInfo.getPackageName());
}
intent.putExtra("connectionID", connectionInfo.getConnectionID());
// start the AppRTCActivity
startActivityForResult(intent, this.sendRequestCode);
}
// activity returns
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
busy = false;
if (requestCode == REQUEST_REFRESHAPPS_QUICK || requestCode == REQUEST_REFRESHAPPS_FULL) {
if (resultCode == RESULT_CANCELED) {
// the activity ended before processing the Apps response
toastShort(R.string.appList_toast_refreshFail);
}
else if (resultCode == RESULT_OK) {
toastShort(R.string.appList_toast_refreshSuccess);
super.onActivityResult(requestCode, RESULT_REPOPULATE, data);
}
else {
// this is probably a result of an AUTH_FAIL, let superclass handle it
super.onActivityResult(requestCode, resultCode, data);
}
}
else if (resultCode == RESULT_CANCELED && requestCode == REQUEST_STARTAPP_FINISH) {
// the user intentionally canceled the activity, and we are supposed to finish this activity after resuming
finish();
}
else // fall back to superclass method
super.onActivityResult(requestCode, resultCode, data);
}
public void onClick_App(View view) {
GridView gridView = (GridView)view.getParent();
int position = gridView.getPositionForView(view);
AppInfo appInfo = (AppInfo)gridView.getItemAtPosition(position);
openApp(appInfo, true); // open the app, resume the activity after returning
}
// this can be triggered by clicking an app in this activity, or by clicking a shortcut on the desktop
protected void openApp(AppInfo appInfo, boolean resume) {
if (appInfo != null) {
this.sendRequestCode = resume ? REQUEST_STARTAPP_RESUME : REQUEST_STARTAPP_FINISH;
this.sendAppInfo = appInfo;
authPrompt(connectionInfo); // utilizes "startActivityForResult", which uses this.sendRequestCode
}
else
toastLong(R.string.appList_toast_notFound);
}
class TabListener<T extends Fragment> implements ActionBar.TabListener {
private AppListFragment mFragment;
private final AppList mActivity;
private final String mTag;
private final Class<T> mClass;
/** Constructor used each time a new tab is created.
* @param activity The host Activity, used to instantiate the fragment
* @param tag The identifier tag for the fragment
* @param clz The fragment's Class, used to instantiate the fragment
*/
public TabListener(AppList activity, String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
}
// called by the parent activity to repopulate the layout of this tab
public void populateLayout() {
if (mFragment != null)
mFragment.populateLayout();
}
/* The following are each of the ActionBar.TabListener callbacks */
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// Check if the fragment is already initialized
if (mFragment == null) {
// If not, instantiate and add it to the activity
mFragment = (AppListFragment)Fragment.instantiate(mActivity, mClass.getName());
ft.add(android.R.id.content, mFragment, mTag);
} else {
// If it exists, simply attach it in order to show it
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// User selected the already selected tab. Usually do nothing.
}
}
}