/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.ui;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.test.espresso.IdlingResource;
import android.support.test.espresso.idling.CountingIdlingResource;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.text.style.TextAppearanceSpan;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import com.facebook.AccessToken;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.FacebookSdk;
import com.facebook.appevents.AppEventsLogger;
import com.facebook.login.LoginManager;
import com.facebook.login.LoginResult;
import org.catrobat.catroid.BuildConfig;
import org.catrobat.catroid.ProjectManager;
import org.catrobat.catroid.R;
import org.catrobat.catroid.common.Constants;
import org.catrobat.catroid.content.Project;
import org.catrobat.catroid.formulaeditor.SensorHandler;
import org.catrobat.catroid.io.LoadProjectTask;
import org.catrobat.catroid.io.LoadProjectTask.OnLoadProjectCompleteListener;
import org.catrobat.catroid.io.StorageHandler;
import org.catrobat.catroid.stage.PreStageActivity;
import org.catrobat.catroid.stage.StageActivity;
import org.catrobat.catroid.transfers.GetFacebookUserInfoTask;
import org.catrobat.catroid.ui.dialogs.NewProjectDialog;
import org.catrobat.catroid.ui.dialogs.SignInDialog;
import org.catrobat.catroid.utils.DownloadUtil;
import org.catrobat.catroid.utils.StatusBarNotificationManager;
import org.catrobat.catroid.utils.ToastUtil;
import org.catrobat.catroid.utils.UtilFile;
import org.catrobat.catroid.utils.UtilUi;
import org.catrobat.catroid.utils.UtilZip;
import org.catrobat.catroid.utils.Utils;
import org.rauschig.jarchivelib.Archiver;
import org.rauschig.jarchivelib.ArchiverFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale;
import java.util.concurrent.locks.Lock;
public class MainMenuActivity extends BaseActivity implements OnLoadProjectCompleteListener {
private static final String TAG = MainMenuActivity.class.getSimpleName();
private static final String START_PROJECT = BuildConfig.START_PROJECT;
private static final Boolean STANDALONE_MODE = BuildConfig.FEATURE_APK_GENERATOR_ENABLED;
private static final String ZIP_FILE_NAME = START_PROJECT + ".zip";
private static final String STANDALONE_PROJECT_NAME = BuildConfig.PROJECT_NAME;
public static final String SHARED_PREFERENCES_SHOW_BROWSER_WARNING = "shared_preferences_browser_warning";
public static final int REQUEST_CODE_GOOGLE_PLUS_SIGNIN = 100;
private static final String TYPE_FILE = "file";
private static final String TYPE_HTTP = "http";
private Lock viewSwitchLock = new ViewSwitchLock();
private CallbackManager callbackManager;
private SignInDialog signInDialog;
private Menu mainMenu;
CountingIdlingResource idlingResource = new CountingIdlingResource(TAG);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!Utils.checkForExternalStorageAvailableAndDisplayErrorIfNot(this)) {
return;
}
initializeFacebookSdk();
PreferenceManager.setDefaultValues(this, R.xml.preferences, true);
UtilUi.updateScreenWidthAndHeight(this);
if (STANDALONE_MODE) {
/*requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);*/
getActionBar().hide();
setContentView(R.layout.activity_main_menu_splashscreen);
unzipProgram();
} else {
setContentView(R.layout.activity_main_menu);
final ActionBar actionBar = getActionBar();
actionBar.setDisplayUseLogoEnabled(true);
actionBar.setHomeButtonEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setTitle(R.string.app_name);
findViewById(R.id.main_menu_button_continue).setEnabled(false);
// Load external project from URL or local file system.
Uri loadExternalProjectUri = getIntent().getData();
getIntent().setData(null);
if (loadExternalProjectUri != null) {
loadProgramFromExternalSource(loadExternalProjectUri);
}
}
}
@Override
protected void onResume() {
super.onResume();
if (!Utils.checkForExternalStorageAvailableAndDisplayErrorIfNot(this)) {
return;
}
AppEventsLogger.activateApp(this);
SettingsActivity.setLegoMindstormsNXTSensorChooserEnabled(this, false);
SettingsActivity.setLegoMindstormsEV3SensorChooserEnabled(this, false);
SettingsActivity.setDroneChooserEnabled(this, false);
findViewById(R.id.progress_circle).setVisibility(View.VISIBLE);
final Activity activity = this;
idlingResource.increment();
Runnable r = new Runnable() {
@Override
public void run() {
UtilFile.createStandardProjectIfRootDirectoryIsEmpty(activity);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
finishOnCreateAfterRunnable();
}
});
}
};
(new Thread(r)).start();
}
@Override
public void onPause() {
super.onPause();
if (!Utils.externalStorageAvailable()) {
return;
}
AppEventsLogger.deactivateApp(this);
Project currentProject = ProjectManager.getInstance().getCurrentProject();
if (currentProject != null) {
ProjectManager.getInstance().saveProject(getApplicationContext());
Utils.saveToPreferences(this, Constants.PREF_PROJECTNAME_KEY, currentProject.getName());
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main_menu, menu);
mainMenu = menu;
final MenuItem scratchConverterMenuItem = menu.findItem(R.id.menu_scratch_converter);
if (scratchConverterMenuItem != null) {
final String title = getString(R.string.main_menu_scratch_converter);
final String beta = getString(R.string.beta).toUpperCase(Locale.getDefault());
final SpannableString spanTitle = new SpannableString(title + " " + beta);
final int begin = title.length() + 1;
final int end = begin + beta.length();
final int betaLabelColor = ContextCompat.getColor(this, R.color.beta_label_color);
spanTitle.setSpan(new ForegroundColorSpan(betaLabelColor), begin, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
scratchConverterMenuItem.setTitle(spanTitle);
}
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem logout = mainMenu.findItem(R.id.menu_logout);
MenuItem login = mainMenu.findItem(R.id.menu_login);
logout.setVisible(Utils.isUserLoggedIn(this));
login.setVisible(!Utils.isUserLoggedIn(this));
if (!BuildConfig.FEATURE_SCRATCH_CONVERTER_ENABLED) {
mainMenu.removeItem(R.id.menu_scratch_converter);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
return true;
}
return super.onOptionsItemSelected(item);
}
private void finishOnCreateAfterRunnable() {
if (!STANDALONE_MODE) {
findViewById(R.id.progress_circle).setVisibility(View.GONE);
findViewById(R.id.main_menu_buttons_container).setVisibility(View.VISIBLE);
PreStageActivity.shutdownPersistentResources();
setMainMenuButtonContinueText();
findViewById(R.id.main_menu_button_continue).setEnabled(true);
}
String projectName = getIntent().getStringExtra(StatusBarNotificationManager.EXTRA_PROJECT_NAME);
if (projectName != null) {
loadProjectInBackground(projectName);
}
getIntent().removeExtra(StatusBarNotificationManager.EXTRA_PROJECT_NAME);
if (ProjectManager.getInstance().getHandleNewSceneFromScriptActivity()) {
Intent intent = new Intent(this, ProjectActivity.class);
intent.putExtra(ProjectActivity.EXTRA_FRAGMENT_POSITION, ProjectActivity.FRAGMENT_SCENES);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
}
idlingResource.decrement();
}
// needed because of android:onClick in activity_main_menu.xml
public void handleContinueButton(View view) {
handleContinueButton();
}
public void handleContinueButton() {
LoadProjectTask loadProjectTask = new LoadProjectTask(this, Utils.getCurrentProjectName(this), true, true);
loadProjectTask.setOnLoadProjectCompleteListener(this);
findViewById(R.id.main_menu_buttons_container).setVisibility(View.GONE);
loadProjectTask.execute();
}
private void loadProjectInBackground(String projectName) {
if (!viewSwitchLock.tryLock()) {
return;
}
LoadProjectTask loadProjectTask = new LoadProjectTask(this, projectName, true, true);
loadProjectTask.setOnLoadProjectCompleteListener(this);
findViewById(R.id.main_menu_buttons_container).setVisibility(View.GONE);
loadProjectTask.execute();
}
@Override
public void onLoadProjectSuccess(boolean startProjectActivity) {
if (STANDALONE_MODE) {
Log.d("STANDALONE", "onLoadProjectSucess -> startStage");
startStageProject();
} else if (ProjectManager.getInstance().getCurrentProject() != null && startProjectActivity) {
Intent intent = new Intent(MainMenuActivity.this, ProjectActivity.class);
startActivity(intent);
}
}
public void handleNewButton(View view) {
if (!viewSwitchLock.tryLock()) {
return;
}
NewProjectDialog dialog = new NewProjectDialog();
dialog.show(getFragmentManager(), NewProjectDialog.DIALOG_FRAGMENT_TAG);
}
public void handleProgramsButton(View view) {
findViewById(R.id.progress_circle).setVisibility(View.VISIBLE);
if (!viewSwitchLock.tryLock()) {
return;
}
Intent intent = new Intent(MainMenuActivity.this, MyProjectsActivity.class);
startActivity(intent);
}
public void handleHelpButton(View view) {
if (!Utils.isNetworkAvailable(view.getContext(), true)) {
return;
}
if (!viewSwitchLock.tryLock()) {
return;
}
startWebViewActivity(Constants.CATROBAT_HELP_URL);
}
public void handleWebButton(View view) {
if (!Utils.isNetworkAvailable(view.getContext(), true)) {
return;
}
if (!viewSwitchLock.tryLock()) {
return;
}
if (Utils.isUserLoggedIn(this)) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
String username = sharedPreferences.getString(Constants.USERNAME, Constants.NO_USERNAME);
String token = sharedPreferences.getString(Constants.TOKEN, Constants.NO_TOKEN);
String url = Constants.CATROBAT_TOKEN_LOGIN_URL + username + Constants.CATROBAT_TOKEN_LOGIN_AMP_TOKEN + token;
startWebViewActivity(url);
} else {
startWebViewActivity(Constants.BASE_URL_HTTPS);
}
}
public void startWebViewActivity(String url) {
Intent intent = new Intent(MainMenuActivity.this, WebViewActivity.class);
intent.putExtra(WebViewActivity.INTENT_PARAMETER_URL, url);
startActivity(intent);
}
public void handleUploadButton(View view) {
if (!Utils.isNetworkAvailable(view.getContext(), true)) {
return;
}
if (!viewSwitchLock.tryLock()) {
return;
}
ProjectManager.getInstance().uploadProject(Utils.getCurrentProjectName(this), this);
}
private void loadProgramFromExternalSource(Uri loadExternalProjectUri) {
String scheme = loadExternalProjectUri.getScheme();
if (scheme.startsWith(TYPE_HTTP)) {
String url = loadExternalProjectUri.toString();
DownloadUtil.getInstance().prepareDownloadAndStartIfPossible(this, url);
} else if (scheme.equals(TYPE_FILE)) {
String path = loadExternalProjectUri.getPath();
int a = path.lastIndexOf('/') + 1;
int b = path.lastIndexOf('.');
String projectName = path.substring(a, b);
if (!UtilZip.unZipFile(path, Utils.buildProjectPath(projectName))) {
Utils.showErrorDialog(this, R.string.error_load_project);
}
}
}
private void setMainMenuButtonContinueText() {
Button mainMenuButtonContinue = (Button) this.findViewById(R.id.main_menu_button_continue);
TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(this, R.style.MainMenuButtonTextSecondLine);
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
String mainMenuContinue = this.getString(R.string.main_menu_continue);
spannableStringBuilder.append(mainMenuContinue);
spannableStringBuilder.append("\n");
spannableStringBuilder.append(Utils.getCurrentProjectName(this));
spannableStringBuilder.setSpan(textAppearanceSpan, mainMenuContinue.length() + 1,
spannableStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
mainMenuButtonContinue.setText(spannableStringBuilder);
}
@Override
public void onLoadProjectFailure() {
findViewById(R.id.main_menu_buttons_container).setVisibility(View.VISIBLE);
}
public void initializeFacebookSdk() {
FacebookSdk.sdkInitialize(getApplicationContext());
callbackManager = CallbackManager.Factory.create();
LoginManager.getInstance().registerCallback(callbackManager,
new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
Log.d(TAG, loginResult.toString());
AccessToken accessToken = loginResult.getAccessToken();
GetFacebookUserInfoTask getFacebookUserInfoTask = new GetFacebookUserInfoTask(MainMenuActivity.this,
accessToken.getToken(), accessToken.getUserId());
getFacebookUserInfoTask.setOnGetFacebookUserInfoTaskCompleteListener(signInDialog);
getFacebookUserInfoTask.execute();
}
@Override
public void onCancel() {
Log.d(TAG, "cancel");
}
@Override
public void onError(FacebookException exception) {
ToastUtil.showError(MainMenuActivity.this, exception.getMessage());
Log.d(TAG, exception.getMessage());
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!STANDALONE_MODE) {
super.onActivityResult(requestCode, resultCode, data);
callbackManager.onActivityResult(requestCode, resultCode, data);
} else {
if (requestCode == PreStageActivity.REQUEST_RESOURCES_INIT && resultCode == RESULT_OK) {
SensorHandler.startSensorListener(this);
Intent intent = new Intent(MainMenuActivity.this, StageActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
intent.addFlags(0x8000); // equal to Intent.FLAG_ACTIVITY_CLEAR_TASK which is only available from API level 11
}
startActivityForResult(intent, StageActivity.STAGE_ACTIVITY_FINISH);
}
if (requestCode == StageActivity.STAGE_ACTIVITY_FINISH) {
finish();
}
}
}
public void setSignInDialog(SignInDialog signInDialog) {
this.signInDialog = signInDialog;
}
private void unzipProgram() {
StorageHandler.getInstance();
String zipFileString = Constants.DEFAULT_ROOT + "/" + ZIP_FILE_NAME;
copyProgramZip();
Log.d("STANDALONE", "default root " + Constants.DEFAULT_ROOT);
Log.d("STANDALONE", "zip file name:" + ZIP_FILE_NAME);
Archiver archiver = ArchiverFactory.createArchiver("zip");
File unpackedDirectory = new File(Constants.DEFAULT_ROOT + "/" + START_PROJECT);
try {
archiver.extract(new File(zipFileString), unpackedDirectory);
} catch (IOException e) {
Log.d("STANDALONE", "Can't extract program", e);
}
File destination = new File(Constants.DEFAULT_ROOT + "/" + STANDALONE_PROJECT_NAME);
if (unpackedDirectory.isDirectory()) {
unpackedDirectory.renameTo(destination);
}
loadStageProject(STANDALONE_PROJECT_NAME);
File zipFile = new File(zipFileString);
if (zipFile.exists()) {
zipFile.delete();
}
}
private void copyProgramZip() {
AssetManager assetManager = getResources().getAssets();
String[] files = null;
try {
files = assetManager.list("");
} catch (IOException e) {
Log.e("STANDALONE", "Failed to get asset file list.", e);
}
for (String filename : files) {
if (filename.contains(ZIP_FILE_NAME)) {
InputStream in;
OutputStream out;
try {
in = assetManager.open(filename);
File outFile = new File(Constants.DEFAULT_ROOT, filename);
out = new FileOutputStream(outFile);
copyFile(in, out);
out.flush();
out.close();
in.close();
} catch (IOException e) {
Log.e("STANDALONE", "Failed to copy asset file: " + filename, e);
}
}
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
private void loadStageProject(String projectName) {
LoadProjectTask loadProjectTask = new LoadProjectTask(this, projectName, false, false);
loadProjectTask.setOnLoadProjectCompleteListener(this);
Log.e("STANDALONE", "going to execute standalone project");
loadProjectTask.execute();
}
private void startStageProject() {
//ProjectManager.getInstance().getCurrentProject().getUserVariables().resetAllUserVariables();
Intent intent = new Intent(this, PreStageActivity.class);
startActivityForResult(intent, PreStageActivity.REQUEST_RESOURCES_INIT);
}
@VisibleForTesting
@NonNull
public IdlingResource getIdlingResource() {
return idlingResource;
}
}