/*
* 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.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.speech.RecognizerIntent;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
import org.catrobat.catroid.R;
import org.catrobat.catroid.common.Constants;
import org.catrobat.catroid.common.ScratchProgramData;
import org.catrobat.catroid.scratchconverter.Client;
import org.catrobat.catroid.scratchconverter.ConversionManager;
import org.catrobat.catroid.scratchconverter.ScratchConversionManager;
import org.catrobat.catroid.scratchconverter.WebSocketClient;
import org.catrobat.catroid.scratchconverter.protocol.WebSocketMessageListener;
import org.catrobat.catroid.ui.fragment.ScratchConverterSlidingUpPanelFragment;
import org.catrobat.catroid.ui.fragment.SearchScratchSearchProjectsListFragment;
import org.catrobat.catroid.utils.ToastUtil;
import org.catrobat.catroid.utils.Utils;
import org.catrobat.catroid.web.ScratchDataFetcher;
import org.catrobat.catroid.web.ServerCalls;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class ScratchConverterActivity extends BaseActivity implements SlidingUpPanelLayout.PanelSlideListener {
private static final String TAG = ScratchConverterActivity.class.getSimpleName();
// to avoid using singleton in fragment
private static Client client = null;
private static ScratchDataFetcher dataFetcher = ServerCalls.getInstance();
private SearchScratchSearchProjectsListFragment searchProjectsListFragment;
private ScratchConverterSlidingUpPanelFragment converterSlidingUpPanelFragment;
private SlidingUpPanelLayout slidingLayout;
private ConversionManager conversionManager;
// dependency-injection for testing with mock object
public static void setDataFetcher(final ScratchDataFetcher fetcher) {
dataFetcher = fetcher;
}
public static void setClient(final Client converterClient) {
client = converterClient;
}
public ScratchConverterSlidingUpPanelFragment getConverterSlidingUpPanelFragment() {
return converterSlidingUpPanelFragment;
}
public SearchScratchSearchProjectsListFragment getSearchProjectsListFragment() {
return searchProjectsListFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scratch_converter);
setUpActionBar();
setReturnByPressingBackButton(true);
searchProjectsListFragment = (SearchScratchSearchProjectsListFragment) getFragmentManager().findFragmentById(
R.id.fragment_scratch_search_projects_list);
searchProjectsListFragment.setDataFetcher(dataFetcher);
converterSlidingUpPanelFragment = (ScratchConverterSlidingUpPanelFragment) getFragmentManager().findFragmentById(
R.id.fragment_scratch_converter_sliding_up_panel);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
final long clientID = settings.getLong(Constants.SCRATCH_CONVERTER_CLIENT_ID_SHARED_PREFERENCE_NAME,
Client.INVALID_CLIENT_ID);
if (client == null) {
client = new WebSocketClient(clientID, new WebSocketMessageListener());
}
conversionManager = new ScratchConversionManager(this, client, false);
conversionManager.setCurrentActivity(this);
conversionManager.addGlobalDownloadCallback(converterSlidingUpPanelFragment);
conversionManager.addBaseInfoViewListener(converterSlidingUpPanelFragment);
conversionManager.addGlobalJobViewListener(converterSlidingUpPanelFragment);
searchProjectsListFragment.setConversionManager(conversionManager);
slidingLayout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout);
slidingLayout.addPanelSlideListener(this);
final int betaLabelColor = ContextCompat.getColor(this, R.color.beta_label_color);
appendColoredBetaLabelToTitle(betaLabelColor);
hideSlideUpPanelBar();
Log.i(TAG, "Scratch Converter Activity created");
}
@Override
protected void onStart() {
super.onStart();
conversionManager.setCurrentActivity(this);
if (!client.isAuthenticated()) {
conversionManager.connectAndAuthenticate();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "Destroyed: " + TAG);
conversionManager.shutdown();
conversionManager.removeGlobalDownloadCallback(converterSlidingUpPanelFragment);
conversionManager.removeBaseInfoViewListener(converterSlidingUpPanelFragment);
conversionManager.removeGlobalJobViewListener(converterSlidingUpPanelFragment);
client = null;
}
private void setUpActionBar() {
final ActionBar actionBar = getActionBar();
actionBar.setTitle(R.string.title_activity_scratch_converter);
actionBar.setHomeButtonEnabled(true);
}
private void appendColoredBetaLabelToTitle(final int color) {
final String title = getString(R.string.title_activity_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();
spanTitle.setSpan(new ForegroundColorSpan(color), begin, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
getActionBar().setTitle(spanTitle);
}
public void convertProjects(List<ScratchProgramData> programList) {
final int numberOfJobsInProgress = conversionManager.getNumberOfJobsInProgress();
if (numberOfJobsInProgress + programList.size() > Constants.SCRATCH_CONVERTER_MAX_NUMBER_OF_JOBS_PER_CLIENT) {
ToastUtil.showError(this, getResources().getQuantityString(
R.plurals.error_cannot_convert_more_than_x_programs,
Constants.SCRATCH_CONVERTER_MAX_NUMBER_OF_JOBS_PER_CLIENT,
Constants.SCRATCH_CONVERTER_MAX_NUMBER_OF_JOBS_PER_CLIENT));
return;
}
int counter = 0;
for (ScratchProgramData programData : programList) {
if (Utils.isDeprecatedScratchProgram(programData)) {
final Date releasePublishedDate = Utils.getScratchSecondReleasePublishedDate();
java.text.DateFormat dateFormat = android.text.format.DateFormat.getMediumDateFormat(this);
ToastUtil.showError(this, getString(R.string.error_cannot_convert_deprecated_scratch_program_x_x,
programData.getTitle(), dateFormat.format(releasePublishedDate)));
continue;
}
if (conversionManager.isJobInProgress(programData.getId())) {
continue;
}
Log.i(TAG, "Converting program: " + programData.getTitle());
conversionManager.convertProgram(programData.getId(), programData.getTitle(), programData.getImage(), false);
counter++;
}
if (counter > 0) {
ToastUtil.showSuccess(this, getResources().getQuantityString(R.plurals.scratch_conversion_scheduled_x,
counter, counter));
}
}
public boolean isSlideUpPanelEmpty() {
return !converterSlidingUpPanelFragment.hasVisibleJobs();
}
public void showSlideUpPanelBar(final long delayMillis) {
final int marginTop = getResources().getDimensionPixelSize(R.dimen.scratch_project_search_list_view_margin_top);
final int marginBottom = getResources().getDimensionPixelSize(
R.dimen.scratch_project_search_list_view_margin_bottom);
if (delayMillis > 0) {
slidingLayout.postDelayed(new Runnable() {
public void run() {
slidingLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
searchProjectsListFragment.setSearchResultsListViewMargin(0, marginTop, 0, marginBottom);
}
}, delayMillis);
} else {
slidingLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
searchProjectsListFragment.setSearchResultsListViewMargin(0, marginTop, 0, marginBottom);
}
}
public void hideSlideUpPanelBar() {
int marginTop = getResources().getDimensionPixelSize(R.dimen.scratch_project_search_list_view_margin_top);
searchProjectsListFragment.setSearchResultsListViewMargin(0, marginTop, 0, 0);
slidingLayout.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_scratch_projects, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
handleShowDetails(searchProjectsListFragment.getShowDetails(),
menu.findItem(R.id.menu_scratch_projects_show_details));
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_scratch_projects_convert:
Log.d(TAG, "Selected menu item 'convert'");
searchProjectsListFragment.startConvertActionMode();
break;
case R.id.menu_scratch_projects_show_details:
Log.d(TAG, "Selected menu item 'Show/Hide details'");
handleShowDetails(!searchProjectsListFragment.getShowDetails(), item);
break;
}
return super.onOptionsItemSelected(item);
}
private void handleShowDetails(boolean showDetails, MenuItem item) {
searchProjectsListFragment.setShowDetails(showDetails);
item.setTitle(showDetails ? R.string.hide_details : R.string.show_details);
}
public void displaySpeechRecognizer() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
startActivityForResult(intent, Constants.INTENT_REQUEST_CODE_SPEECH);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Constants.INTENT_REQUEST_CODE_SPEECH && resultCode == RESULT_OK) {
List<String> results = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
String spokenText = results.get(0);
searchProjectsListFragment.searchAndUpdateText(spokenText);
} else if (requestCode == Constants.INTENT_REQUEST_CODE_CONVERT && resultCode == RESULT_OK) {
if (!data.hasExtra(Constants.INTENT_SCRATCH_PROGRAM_DATA)) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
final ScratchProgramData projectData = data.getParcelableExtra(Constants.INTENT_SCRATCH_PROGRAM_DATA);
final List<ScratchProgramData> projectList = new ArrayList<>();
projectList.add(projectData);
convertProjects(projectList);
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onPanelSlide(View panel, float slideOffset) {
converterSlidingUpPanelFragment.rotateImageButton(slideOffset * 180.0f);
}
@Override
public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState,
SlidingUpPanelLayout.PanelState newState) {
Log.d(TAG, "SlidingUpPanel state changed: " + newState.toString());
switch (newState) {
case EXPANDED:
converterSlidingUpPanelFragment.rotateImageButton(180);
converterSlidingUpPanelFragment.scrollUpPanelScrollView();
break;
case COLLAPSED:
converterSlidingUpPanelFragment.rotateImageButton(0);
converterSlidingUpPanelFragment.scrollUpPanelScrollView();
break;
}
}
}