/*
* Copyright 2015 Daniel Dittmar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file 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 dan.dit.whatsthat.system;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.InputType;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.OvershootInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.github.johnpersano.supertoasts.SuperToast;
import com.plattysoft.leonids.ParticleSystem;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.security.Key;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import dan.dit.whatsthat.BuildConfig;
import dan.dit.whatsthat.R;
import dan.dit.whatsthat.achievement.AchievementDataEvent;
import dan.dit.whatsthat.achievement.AchievementManager;
import dan.dit.whatsthat.achievement.AchievementProperties;
import dan.dit.whatsthat.image.Image;
import dan.dit.whatsthat.image.ImageManager;
import dan.dit.whatsthat.preferences.User;
import dan.dit.whatsthat.riddle.Riddle;
import dan.dit.whatsthat.riddle.RiddleInitializer;
import dan.dit.whatsthat.riddle.RiddleMaker;
import dan.dit.whatsthat.riddle.RiddleManager;
import dan.dit.whatsthat.riddle.RiddleView;
import dan.dit.whatsthat.riddle.UnsolvedRiddlesChooser;
import dan.dit.whatsthat.riddle.achievement.holders.MiscAchievementHolder;
import dan.dit.whatsthat.riddle.achievement.holders.TestSubjectAchievementHolder;
import dan.dit.whatsthat.riddle.control.GameWelcomeDialog;
import dan.dit.whatsthat.riddle.control.RiddleGame;
import dan.dit.whatsthat.riddle.types.PracticalRiddleType;
import dan.dit.whatsthat.solution.SolutionInput;
import dan.dit.whatsthat.solution.SolutionInputListener;
import dan.dit.whatsthat.solution.SolutionInputView;
import dan.dit.whatsthat.storage.ImageTable;
import dan.dit.whatsthat.storage.ImagesContentProvider;
import dan.dit.whatsthat.system.store.StoreActivity;
import dan.dit.whatsthat.testsubject.TestSubject;
import dan.dit.whatsthat.testsubject.TestSubjectToast;
import dan.dit.whatsthat.util.general.PercentProgressListener;
import dan.dit.whatsthat.util.general.SimpleCrypto;
import dan.dit.whatsthat.util.image.Dimension;
import dan.dit.whatsthat.util.image.ExternalStorage;
import dan.dit.whatsthat.util.wallet.Wallet;
import dan.dit.whatsthat.util.wallet.WalletEntry;
/**
* Created by daniel on 10.04.15.
*/
public class RiddleFragment extends Fragment implements PercentProgressListener, LoaderManager.LoaderCallbacks<Cursor>, SolutionInputListener,
UnsolvedRiddlesChooser.Callback, NoPanicDialog.Callback, RiddleView.PartyCallback {
public static final Map<String, Image> ALL_IMAGES = new HashMap<>();
private static final String PRE_ENCRYPTED_COMPLAIN = "Image: ";
private static final String POST_ENCRYPTED_COMPLAIN = "EndImage";
private RiddleManager mManager;
private RiddleView mRiddleView;
private SolutionInputView mSolutionView;
private PercentProgressListener mProgressBar;
private ImageButton mBtnRiddles;
private ImageView mOpenStore;
private Iterator<Long> mOpenUnsolvedRiddlesId;
private RiddlePickerDialog mRiddlePickerDialog;
private boolean mErrorHandlingAttempted;
private ImageButton mBtnCheat;
private TextView mScoreInfo;
private ImageView mBtnPanic;
private Handler mMainHandler;
private TestSubjectAchievementHolder.UnclaimedAchievementsCountListener mUnclaimedChangedListener;
private Wallet.OnEntryChangedListener mScoreChangedListener;
private Animation mBtnPanicPressedAnim;
private View mOpenStoreArrow;
private Animation mOpenStoreArrowAnim;
public void onProgressUpdate(int progress) {
mProgressBar.onProgressUpdate(progress);
}
private boolean isTotalMenuEnabled() {
return !ImageManager.isSyncing() && !mManager.isMakingRiddle();
}
private void updateMenuButtons() {
mBtnRiddles.setEnabled(isTotalMenuEnabled());
mBtnRiddles.setImageResource(TestSubject.getInstance().getImageResId());
mBtnPanic.setEnabled(isTotalMenuEnabled());
}
private int mLastDisplayedScoreInfo = -1;
private boolean mNewScoreInfoAnimating = false;
private long mLastNewScoreAnimEndedTimestamp;
private Animation mNewScoreInfo;
private Animation.AnimationListener mNewScoreInfoAnimListener;
private Animation mScoreInfoFadeOut;
private void initNewScoreInfo() {
mScoreInfo.setVisibility(View.INVISIBLE);
mNewScoreInfo = AnimationUtils.loadAnimation(getActivity(), R.anim.score_info_newdata);
mNewScoreInfoAnimListener = new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mNewScoreInfoAnimating = true;
mScoreInfo.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animation animation) {
mNewScoreInfoAnimating = false;
mLastNewScoreAnimEndedTimestamp = System.currentTimeMillis();
mScoreInfo.startAnimation(mScoreInfoFadeOut);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
};
mNewScoreInfo.setAnimationListener(mNewScoreInfoAnimListener);
mScoreInfoFadeOut = AnimationUtils.loadAnimation(getActivity(), R.anim.score_info_fadeout);
}
private void updateScoreInfo(boolean animate) {
if (mScoreInfo != null) {
int currentScoreInfo = TestSubject.getInstance().getCurrentScore();
if (currentScoreInfo != mLastDisplayedScoreInfo && currentScoreInfo != 0) {
// only new info and not necessarily at start when there is no score
mScoreInfo.setText(String.valueOf(currentScoreInfo));
if (animate && !mNewScoreInfoAnimating
&& System.currentTimeMillis() - mLastNewScoreAnimEndedTimestamp >= 10000L) {
// when not currently animating and didn't animate in the last some seconds,
// start the animation
mLastDisplayedScoreInfo = currentScoreInfo;
mScoreInfo.clearAnimation(); // in case it is fading out
mScoreInfo.startAnimation(mNewScoreInfo);
}
}
}
}
private void nextRiddleIfEmpty() {
if (!mRiddleView.hasController()) {
Log.d("Riddle", "Next riddle as it is empty.");
long suggestedId = Riddle.getLastVisibleRiddleId(getActivity().getApplicationContext());
if (suggestedId != Riddle.NO_ID || (mManager.getUnsolvedRiddleCount() > 0)) {
nextUnsolvedRiddle(suggestedId);
} else {
nextRiddle();
}
}
}
private void onRiddleMade(RiddleGame riddle, boolean newRiddle) {
mProgressBar.onProgressUpdate(0);
riddle.initViews(mRiddleView, mSolutionView, this);
updateMenuButtons();
if (mRiddleView != null && mRiddleView.hasController()) {
mErrorHandlingAttempted = false; // clear flag
long currRiddleId = mRiddleView.getRiddleId();
if (currRiddleId <= Riddle.NO_ID) {
Log.e("Riddle", "Got riddle with no id and still no id: " + currRiddleId + " riddle " + riddle);
}
PracticalRiddleType currRiddleType = mRiddleView.getRiddleType();
Riddle.saveLastVisibleRiddleId(getActivity().getApplicationContext(), currRiddleId);
Riddle.saveLastVisibleRiddleType(getActivity().getApplicationContext(), currRiddleType);
if (newRiddle) {
checkedShowHintDialog(mRiddleView.getRiddleType());
} else if (TestSubject.isInitialized() && !TestSubject.getInstance().canSkip()
&& mSolutionView != null) {
mSolutionView.provideHint(SolutionInput.HINT_LEVEL_MINIMAL);
}
if (mSolutionView != null &&
mSolutionView.getProvidedHintLevel() > SolutionInput.HINT_LEVEL_NONE) {
mRiddleView.forbidRiddleBonusScore();
}
}
}
private void checkedShowHintDialog(PracticalRiddleType type) {
if (type == null) {
return;
}
if (TestSubject.getInstance().hasAvailableHint(type)) {
GameWelcomeDialog dialog = GameWelcomeDialog.makeInstance(type);
dialog.show(getFragmentManager(), GameWelcomeDialog.GAME_WELCOME_DIALOG_TAG);
}
}
private void handleError(Image image, Riddle riddle) {
if (image != null) {
if (TextUtils.isEmpty(image.getRelativePath()) || ExternalStorage.isMounted()) {
Log.e("Riddle", "No bitmap for image " + image + " has no relative image path or has one but storage is mounted -> removing from database.");
ImageManager.removeInvalidImageImmediately(getActivity(), image); // will update cursor and therefore list of images
} else if (mErrorHandlingAttempted) {
Toast.makeText(getActivity(), R.string.handle_error_attempted, Toast.LENGTH_SHORT).show();
}
}
if (riddle != null) {
Log.e("Riddle", "Riddle on error. " + riddle + " removing from unsolved and database.");
mManager.onRiddleInvalidated(riddle);
Riddle.deleteFromDatabase(getActivity(), riddle.getId());
}
if (!mErrorHandlingAttempted) {
mErrorHandlingAttempted = true;
findSomeRiddle();
}
}
private Dimension makeRiddleDimension() {
return new Dimension(mRiddleView.getWidth(), mRiddleView.getHeight());
}
private void remakeCurrentRiddle() {
if (!isTotalMenuEnabled()) {
updateMenuButtons();
return;
}
if (!mRiddleView.hasController()) {
return;
}
Riddle current = mRiddleView.getRiddle();
if (current == null) {
return;
}
// try to find a new riddle type:
PracticalRiddleType newType = TestSubject.getInstance().getTypesController().findNextRiddleType(true, current
.getType());
if (newType == null || newType.equals(current.getType())) {
newType = TestSubject.getInstance().getTypesController().findNextRiddleType(false, current.getType());
}
if (newType == null) {
Toast.makeText(getActivity(), R.string.panic_retry_failed_no_types, Toast.LENGTH_SHORT).show();
return; // only found the excluded one
}
clearRiddle();
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
mManager.remakeCurrentWithNewType(getActivity().getApplicationContext(),
current,
newType,
makeRiddleDimension(), displaymetrics.densityDpi,
new RiddleMaker.RiddleMakerListener() {
@Override
public void onProgressUpdate(int progress) {
RiddleFragment.this.onProgressUpdate(progress);
}
@Override
public void onRiddleReady(RiddleGame riddle) {
onRiddleMade(riddle, true);
playStartRiddleAnimation();
AchievementProperties data = TestSubject.getInstance()
.getAchievementHolder().getMiscData();
if (data != null) {
// notify achievements that a riddle was remade and how many times
// for this riddle
data.enableSilentChanges(AchievementDataEvent.EVENT_TYPE_DATA_UPDATE);
data.putValue(MiscAchievementHolder.KEY_REMADE_RIDDLE_CURRENT_REMADE_COUNT,
(long) riddle.getRemadeCount(),
AchievementProperties.UPDATE_POLICY_ALWAYS);
data.increment(MiscAchievementHolder.KEY_REMADE_RIDDLE_CURRENT_COUNT,
1L, 0L);
data.disableSilentChanges();
}
}
@Override
public void onError(Image image, Riddle riddle) {
Log.e("HomeStuff", "Riddle maker on error.");
handleError(image, riddle);
RiddleFragment.this.onProgressUpdate(0);
updateMenuButtons();
}
});
updateMenuButtons();
}
private void nextRiddle() {
if (!isTotalMenuEnabled()) {
updateMenuButtons();
return;
}
clearRiddle();
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
mOpenUnsolvedRiddlesId = null;
mManager.makeRiddle(getActivity().getApplicationContext(), findNextRiddleType(),
makeRiddleDimension(), displaymetrics.densityDpi,
new RiddleMaker.RiddleMakerListener() {
@Override
public void onProgressUpdate(int progress) {
RiddleFragment.this.onProgressUpdate(progress);
}
@Override
public void onRiddleReady(RiddleGame riddle) {
onRiddleMade(riddle, true);
mRiddleView.getRiddleType().getAchievementData(AchievementManager.getInstance()).onNewGame();
playStartRiddleAnimation();
}
@Override
public void onError(Image image, Riddle riddle) {
Log.e("HomeStuff", "Riddle maker on error.");
handleError(image, riddle);
RiddleFragment.this.onProgressUpdate(0);
updateMenuButtons();
}
});
updateMenuButtons();
}
private void playStartRiddleAnimation() {
final long inputAnimationDelay = 500L;
final long inputAnimation = 500L;
mSolutionView.setVisibility(View.INVISIBLE);
mSolutionView.clearAnimation();
TranslateAnimation moveIn = new TranslateAnimation(Animation.ABSOLUTE, 0.f, Animation.ABSOLUTE, 0.f, Animation.RELATIVE_TO_SELF, 1.f, Animation.RELATIVE_TO_SELF, 0.f);
moveIn.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mSolutionView.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
moveIn.setInterpolator(new OvershootInterpolator(2.5f));
moveIn.setStartOffset(inputAnimationDelay);
moveIn.setDuration(inputAnimation);
mSolutionView.startAnimation(moveIn);
}
private void nextCheatedRiddle(Image image) {
if (!isTotalMenuEnabled() || image == null) {
return;
}
if (mRiddleView.hasController()) {
clearRiddle();
}
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
mManager.makeSpecific(getActivity().getApplicationContext(), image, findNextRiddleType(), makeRiddleDimension(), displaymetrics.densityDpi,
new RiddleMaker.RiddleMakerListener() {
@Override
public void onProgressUpdate(int progress) {
RiddleFragment.this.onProgressUpdate(progress);
}
@Override
public void onRiddleReady(RiddleGame riddle) {
onRiddleMade(riddle, true);
playStartRiddleAnimation();
}
@Override
public void onError(Image image, Riddle riddle) {
Log.e("HomeStuff", "Cheated Riddle maker on error.");
handleError(image, riddle);
RiddleFragment.this.onProgressUpdate(0);
updateMenuButtons();
}
}
);
updateMenuButtons();
}
private void nextUnsolvedRiddle(long suggestedId) {
if (!isTotalMenuEnabled()) {
return;
}
if (mManager.getUnsolvedRiddleCount() == 0) {
nextRiddle();
return;
}
clearRiddle();
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
mManager.remakeOld(getActivity().getApplicationContext(), suggestedId, makeRiddleDimension(), displaymetrics.densityDpi,
new RiddleMaker.RiddleMakerListener() {
@Override
public void onProgressUpdate(int progress) {
RiddleFragment.this.onProgressUpdate(progress);
}
@Override
public void onRiddleReady(RiddleGame riddle) {
onRiddleMade(riddle, false);
//playStartRiddleAnimation(mBtnUnsolvedRiddles);
}
@Override
public void onError(Image image, Riddle riddle) {
Log.e("HomeStuff", "Unsolved Riddle maker on error.");
handleError(image, riddle);
RiddleFragment.this.onProgressUpdate(0);
updateMenuButtons();
}
}
);
updateMenuButtons();
}
private void clearRiddle() {
if (mRiddleView.hasController()) {
mRiddleView.removeController();
}
mSolutionView.setSolutionInput(null, null);
mSolutionView.clearListener();
}
@Override
public void giveCandy(TestSubjectToast candyToast) {
Animation anim = AnimationUtils.loadAnimation(getActivity(), R.anim.solution_complete);
mSolutionView.startAnimation(anim);
long delay = 0L;
if (candyToast != null) {
SuperToast toast = candyToast.makeSuperToast(getActivity());
toast.show();
delay = toast.getDuration() / 2L;
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
findSomeRiddle();
}
}, delay);
}
private PracticalRiddleType findNextRiddleType() {
return TestSubject.getInstance().getTypesController().findNextRiddleType();
}
private void findSomeRiddle() {
Log.d("Riddle", "Find some riddle.");
if (mOpenUnsolvedRiddlesId != null && mOpenUnsolvedRiddlesId.hasNext()) {
long nextId = mOpenUnsolvedRiddlesId.next();
mOpenUnsolvedRiddlesId.remove();
nextUnsolvedRiddle(nextId);
} else {
nextRiddle();
}
}
@Override
public boolean onSolutionComplete(String userWord) {
if (mRiddleView.hasController()) {
mRiddleView.checkParty(getResources(), this);
mRiddleView.removeController();
}
mSolutionView.clearListener();
return true;
}
@Override
public void onSolutionIncomplete() {
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mRiddleView = (RiddleView) getView().findViewById(R.id.riddle_view);
mProgressBar = (PercentProgressListener) getView().findViewById(R.id.progress_bar);
mSolutionView = (SolutionInputView) getView().findViewById(R.id.solution_input_view);
mOpenStoreArrow = getView().findViewById(R.id.open_store_arrow);
mOpenStoreArrow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onOpenStore();
}
});
mOpenStore = (ImageView) getView().findViewById(R.id.open_store);
mOpenStoreArrowAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.open_store_arrow);
mOpenStore.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mOpenStoreArrow.clearAnimation();
mOpenStoreArrow.startAnimation(mOpenStoreArrowAnim);
break;
case MotionEvent.ACTION_UP:
float checkX = event.getX() + v.getLeft();
float checkY = event.getY() + v.getTop();
if (checkX >= v.getLeft() && checkX <= v.getRight()
&& checkY >= v.getTop() && checkY <= v.getBottom()) {
v.performClick();
}
// fall through!
case MotionEvent.ACTION_CANCEL:
mOpenStoreArrow.clearAnimation();
break;
}
return true;
}
});
mOpenStore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onOpenStore();
}
});
mBtnRiddles = (ImageButton) getView().findViewById(R.id.riddle_make_next);
mScoreInfo = (TextView) getView().findViewById(R.id.currency);
// only allow cheats in debug build
final boolean allowCheats = BuildConfig.DEBUG;
if (allowCheats) {
/*mScoreInfo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mClickCount == 0 || System.currentTimeMillis() - mFirstClickTime > 2000L) {
mClickCount = 0;
mFirstClickTime = System.currentTimeMillis();
}
mClickCount++;
if (mClickCount >= 10) {
mBtnCheat.setVisibility(View.VISIBLE);
}
}
});
mScoreInfo.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mBtnCheat.setVisibility(View.VISIBLE);
return false;
}
});*/
getView().findViewById(R.id.riddle_cheat).setVisibility(View.VISIBLE);
}
mBtnRiddles.setEnabled(false);
mBtnRiddles.setImageResource(TestSubject.getInstance().getImageResId());
mBtnRiddles.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onRiddlesClick();
}
});
mBtnPanic = (ImageView) getView().findViewById(R.id.riddle_hint);
mBtnPanicPressedAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.panic_menu_press);
mBtnPanic.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mBtnPanic.startAnimation(mBtnPanicPressedAnim);
break;
case MotionEvent.ACTION_UP:
float checkX = event.getX() + v.getLeft();
float checkY = event.getY() + v.getTop();
if (checkX >= v.getLeft() && checkX <= v.getRight()
&& checkY >= v.getTop() && checkY <= v.getBottom()) {
v.performClick();
}
// fall through!
case MotionEvent.ACTION_CANCEL:
mBtnPanic.clearAnimation();
break;
}
return true;
}
});
mBtnPanic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onPanic();
}
});
mBtnCheat = (ImageButton) getView().findViewById(R.id.riddle_cheat);
if (allowCheats) {
mBtnCheat.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onCheat();
}
});
} else {
mBtnCheat.setVisibility(View.GONE);
}
mManager = RiddleInitializer.INSTANCE.getRiddleManager();
initNewScoreInfo();
}
private void onOpenStore() {
if (mOpenStore.isEnabled()) {
mOpenStore.setEnabled(false);
cleanUp();
mRiddleView.onPause();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent i = new Intent(getActivity(), StoreActivity.class);
getActivity().startActivity(i);
getActivity().overridePendingTransition(R.anim.store_enter, R.anim.riddles_exit);
}
}, 150L);
}
}
private void onPanic() {
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHH
Bundle args = new Bundle();
mRiddleView.supplyNoPanicParams(args);
mSolutionView.supplyNoPanicParams(args);
NoPanicDialog dialog = new NoPanicDialog();
dialog.setArguments(args);
FragmentTransaction t = getFragmentManager().beginTransaction();
dialog.show(t, "PanicDialog");
}
@Override
public void openUnsolvedRiddle(Collection<Long> toOpen) {
if (toOpen != null && toOpen.size() > 0) {
mOpenUnsolvedRiddlesId = toOpen.iterator();
findSomeRiddle();
}
}
@Override
public boolean canSkip() {
return mRiddleView != null && mRiddleView.hasController() && TestSubject.getInstance().canSkip();
}
@Override
public void onSkip() {
mOpenUnsolvedRiddlesId = null;
nextRiddle();
}
private void decryptComplain() {
Key privateKey = SimpleCrypto.getDeveloperPrivateKey();
if (privateKey == null) {
Toast.makeText(getActivity(), "Kein Schlüssel gefunden.", Toast.LENGTH_SHORT).show();
return;
}
File complainFile = new File(ExternalStorage.getExternalStoragePathIfMounted(null) + "/kummerkasten.txt");
if (!complainFile.exists()) {
Toast.makeText(getActivity(), "Benötigt Datei " + complainFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
return;
}
FileReader reader = null;
String data = null;
try {
reader = new FileReader(complainFile);
int read;
StringBuilder builder = new StringBuilder();
char[] buffer = new char[64];
while ((read = reader.read(buffer)) > 0) {
builder.append(buffer, 0, read);
}
data = builder.toString();
} catch (IOException ioe) {
Toast.makeText(getActivity(), "Fehler beim Lesen der kummerkasten Datei", Toast.LENGTH_SHORT).show();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ioe) {
//ignore
}
}
}
if (data == null) {
return;
}
int start = data.lastIndexOf(PRE_ENCRYPTED_COMPLAIN);
int end = data.lastIndexOf(POST_ENCRYPTED_COMPLAIN);
if (start >= 0 && end >= 0 && start + PRE_ENCRYPTED_COMPLAIN.length() <= end) {
String encrypted = data.substring(start + PRE_ENCRYPTED_COMPLAIN.length(), end);
String decrypted = SimpleCrypto.decrypt(privateKey, encrypted);
String result = data.substring(0, start + PRE_ENCRYPTED_COMPLAIN.length())
+ decrypted
+ POST_ENCRYPTED_COMPLAIN
+ data.substring(end + POST_ENCRYPTED_COMPLAIN.length(), data.length());
FileWriter writer = null;
try {
writer = new FileWriter(complainFile);
writer.write(result);
Toast.makeText(getActivity(), "Beschwerde bereit.. ;)", Toast.LENGTH_SHORT).show();
} catch (IOException ioe) {
Toast.makeText(getActivity(), "Fehler beim Schreiber der entschlüsselten Beschwerde", Toast.LENGTH_SHORT).show();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException ioe) {
//ignore
}
}
}
} else {
Toast.makeText(getActivity(), "Verschlüsselter Bereich nicht gefunden.", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onComplain(Image image) {
if (mRiddleView == null) {
return;
}
if (!mRiddleView.hasController()) {
Toast.makeText(getActivity(), R.string.panic_complain_nothing, Toast.LENGTH_SHORT).show();
return;
}
try {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("plain/text");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{TestSubject.EMAIL_FEEDBACK});
intent.putExtra(Intent.EXTRA_SUBJECT, "WhatsThat complain");
StringBuilder builder = new StringBuilder();
builder.append(getResources().getString(R.string.panic_complain_content))
.append("\n\nMetadata:\n")
.append("Type: ").append(mRiddleView.getRiddleType().getFullName()).append("\n")
.append("Riddle size: ").append(mRiddleView.getWidth()).append("x").append(mRiddleView.getHeight()).append("\n")
.append("Version: ").append(getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionName).append("\n");
if (image != null) {
builder.append("Image author: ").append(image.getAuthor().getName()).append("\n");
Key publicKey = SimpleCrypto.getDeveloperPublicKey();
if (publicKey != null) {
builder.append(PRE_ENCRYPTED_COMPLAIN).append(SimpleCrypto.encrypt(publicKey, System.currentTimeMillis() + ": " + image.getOrigin() + " " + image.getObfuscation() + " " + image.getName() + " " + image.getRelativePath() + " " + image.getHash()))
.append(POST_ENCRYPTED_COMPLAIN).append("\n");
}
}
intent.putExtra(Intent.EXTRA_TEXT, builder.toString());
startActivity(intent);
} catch (Exception e) {
Log.e("HomeStuff", "Exception during complaining, better pretend or user gets mad :D " + e);
Toast.makeText(getActivity(), R.string.panic_complain_dummy_toast, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onRetryWithDifferentRiddle() {
remakeCurrentRiddle();
TestSubject.getInstance().getAchievementHolder().getMiscData().increment
(MiscAchievementHolder.KEY_RETRYING_RIDDLE_COUNT, 1L, 0L);
}
private void showRiddlesDialog() {
Bundle args = new Bundle();
if (mRiddleView.hasController()) {
args.putLong(Riddle.LAST_VISIBLE_UNSOLVED_RIDDLE_ID_KEY, mRiddleView.getRiddleId());
}
if (mRiddlePickerDialog != null) {
mRiddlePickerDialog.dismiss();
}
mRiddlePickerDialog = new RiddlePickerDialog();
mRiddlePickerDialog.setArguments(args);
mRiddlePickerDialog.show(getFragmentManager(), "RiddlePickerDialog");
}
@SuppressWarnings("deprecation")
private void onCheat() {
final EditText input = new EditText(getActivity());
input.setInputType(android.text.InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
new AlertDialog.Builder(getActivity())
.setTitle("CheatBox")
.setMessage("Ch34t0r's h4ckZ:")
.setView(input)
.setPositiveButton("YEAH BABY", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String trimmedText = input.getText().toString().trim();
String value = trimmedText.toLowerCase();
Image selected = null;
for (Image image : ALL_IMAGES.values()) {
if (image.getName().equalsIgnoreCase(value)) {
selected = image;
break;
}
}
if (selected != null) {
nextCheatedRiddle(selected);
} else {
if (value.matches("[0-9]+")) {
int money = 0;
try {
money = Integer.parseInt(value);
} catch (NumberFormatException nfe) {
//ignore
}
if (money > 0) {
TestSubject.getInstance().addAchievementScore(money);
Toast.makeText(getActivity(), "You got rich boy.", Toast.LENGTH_SHORT).show();
}
} else if (value.equalsIgnoreCase("kummerkasten")) {
decryptComplain();
} else if (value.equalsIgnoreCase("oneup")) {
if (TestSubject.getInstance().levelUp()) {
Toast.makeText(getActivity(), "Level up!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), "Höher geht nicht...", Toast.LENGTH_SHORT).show();
}
} else if (value.equalsIgnoreCase("hide")) {
mBtnCheat.setVisibility(View.GONE);
} else if (value.equalsIgnoreCase("party")) {
doParty(0);
} else if (value.equalsIgnoreCase("boom")) {
throw new IllegalArgumentException("Boom.");
} else if (value.equalsIgnoreCase("sudo")) {
User.getInstance().givePermission(User.PERMISSION_SUPER_USER);
Toast.makeText(getActivity(), "You are now a local god", Toast.LENGTH_SHORT).show();
} else if (value.equalsIgnoreCase("sodu")) {
User.getInstance().removePermission(User.PERMISSION_SUPER_USER);
Toast.makeText(getActivity(), "Local god mode disabled", Toast.LENGTH_SHORT).show();
} else if (PracticalRiddleType.getInstance(trimmedText) != null) {
if (TestSubject.getInstance().addNewType(PracticalRiddleType.getInstance(trimmedText))) {
Toast.makeText(getActivity(), "Experiment " + trimmedText + " jetzt verfügbar.", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), "Experiment " + trimmedText + " bereits vorhanden!", Toast.LENGTH_LONG).show();
}
} else if (trimmedText.endsWith(".xml")) {
if (ImageManager.calculateImagedataDeveloperFromFile(getActivity(), trimmedText)) {
Toast.makeText(getActivity(), "XML Berechnung erfolgreich!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), "XML Berechnung fehlgeschlagen!", Toast.LENGTH_LONG).show();
}
} else if (trimmedText.equalsIgnoreCase("calculate imagedata")) {
if (ImageManager.calculateImagedataDeveloper(getActivity())) {
Toast.makeText(getActivity(), "XML Berechnung erfolgreich!!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), "XML Berechnung fehlgeschlagen!!", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(getActivity(), "'" + value + "' nicht gefunden.", Toast.LENGTH_SHORT).show();
}
}
}
}).setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
}
@Override
public void doParty(int partyParam) {
final int emittingTime = 3000;
final long particleLifeTime = 1500L;
final int konfettiPerEmitterPerSecond = 5 + Math.max(partyParam, 0) * 3;
final int konfettiPerEmitter = (int) (particleLifeTime *
konfettiPerEmitterPerSecond /
1000L);
final ViewGroup parent = (ViewGroup) getView();
final View emitAtView = mBtnRiddles;
final int emitGravity = Gravity.BOTTOM;
if (parent == null || emitAtView == null) {
return;
}
new ParticleSystem(getResources(), konfettiPerEmitter, R.drawable.konfetti_long1,
particleLifeTime, parent)
.setScaleRange(0.7f, 1.3f)
.setSpeedModuleAndAngleRange(0.07f, 0.12f, 0, 180)
.setRotationSpeedRange(-90, 90)
.setAcceleration(0.00013f, 90)
.setFadeOut(200, new AccelerateInterpolator())
.emitWithGravity(emitAtView, emitGravity, konfettiPerEmitterPerSecond, emittingTime);
new ParticleSystem(getResources(), konfettiPerEmitter, R.drawable.konfetti_long2,
particleLifeTime, parent)
.setScaleRange(0.7f, 1.3f)
.setSpeedModuleAndAngleRange(0.07f, 0.12f, 0, 180)
.setRotationSpeedRange(-90, 90)
.setAcceleration(0.00013f, 90)
.setFadeOut(200, new AccelerateInterpolator())
.emitWithGravity(emitAtView, emitGravity, konfettiPerEmitterPerSecond, emittingTime);
new ParticleSystem(getResources(), konfettiPerEmitter, R.drawable.konfetti_small1,
particleLifeTime, parent)
.setSpeedModuleAndAngleRange(0.07f, 0.12f, 0, 180)
.setAcceleration(0.00013f, 90)
.setFadeOut(200, new AccelerateInterpolator())
.emitWithGravity(emitAtView, emitGravity, konfettiPerEmitterPerSecond, emittingTime);
new ParticleSystem(getResources(), konfettiPerEmitter, R.drawable.konfetti_small2,
particleLifeTime, parent)
.setSpeedModuleAndAngleRange(0.07f, 0.12f, 0, 180)
.setAcceleration(0.00013f, 90)
.setFadeOut(200, new AccelerateInterpolator())
.emitWithGravity(emitAtView, emitGravity, konfettiPerEmitterPerSecond, emittingTime);
}
@Override
public void showMoneyEarned(int earned) {
final ViewGroup parent = (ViewGroup) getView();
final View emitAtView = mRiddleView;
final int emitGravity = Gravity.CENTER;
if (parent == null || emitAtView == null || earned <= 0) {
return;
}
new ParticleSystem(getResources(), earned, R.drawable.think_currency,
3000L, parent)
.setScaleRange(0.8f, 1.2f)
.setSpeedModuleAndAngleRange(0.0005f, 0.001f, 0, 360)
.setAccelerationModuleAndAndAngleRange(0.00009f, 0.0001f, 275, 310)
.setFadeOut(200, new AccelerateInterpolator())
.emitWithGravity(emitAtView, emitGravity, earned, 1000);
}
private void onRiddlesClick() {
if (!isTotalMenuEnabled()) {
mBtnRiddles.setEnabled(false);
return;
}
updateScoreInfo(true);
showRiddlesDialog();
}
@Override
public void onStart() {
super.onStart();
mMainHandler = new Handler();
mErrorHandlingAttempted = false;
getLoaderManager().initLoader(0, null, this);
updateScoreInfo(false);
mScoreChangedListener = new Wallet.OnEntryChangedListener() {
@Override
public void onDataEvent(WalletEntry entry) {
updateScoreInfo(true);
}
@Override
public void onEntryRemoved(WalletEntry entry) {
}
};
TestSubject.getInstance().registerScoreChangedListener(mScoreChangedListener);
mUnclaimedChangedListener = new TestSubjectAchievementHolder.UnclaimedAchievementsCountListener() {
@Override
public void onDataEvent(Void nothing) {
handleUnclaimedAchievementsCountChanged(TestSubject.getInstance()
.getAchievementHolder().getUnclaimedAchievementsCount());
}
};
TestSubject.getInstance().getAchievementHolder().addUnclaimedAchievementsCountListener
(mUnclaimedChangedListener);
handleUnclaimedAchievementsCountChanged(TestSubject.getInstance().getAchievementHolder()
.getUnclaimedAchievementsCount());
}
@Override
public void onPause() {
super.onPause();
mRiddleView.onPause();
}
@Override
public void onResume() {
super.onResume();
mOpenStore.setEnabled(true);
mRiddleView.onResume();
updateMenuButtons();
}
// needs to be robust against multiple calls
private void cleanUp() {
AchievementManager.commit();
if (mScoreInfo != null) {
mScoreInfo.clearAnimation();
mScoreInfo.setVisibility(View.INVISIBLE);
}
}
@Override
public void onStop() {
super.onStop();
mManager.cancelMakeRiddle();
TestSubject.getInstance().removeScoreChangedListener(mScoreChangedListener);
TestSubject.getInstance().getAchievementHolder().removeUnclaimedAchievementsCountListener
(mUnclaimedChangedListener);
if (mRiddleView != null) {
mRiddleView.setVisibility(View.INVISIBLE); // else it is black when being reloaded
// after having opened the shop
clearRiddle();
}
cleanUp();
Log.d("Riddle", "Stopping riddle fragment");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.riddle_home, null);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created
return new CursorLoader(getActivity(), ImagesContentProvider.CONTENT_URI_IMAGE, ImageTable.ALL_COLUMNS, null, null, ImageTable.COLUMN_TIMESTAMP);
}
private volatile AsyncTask mLoadedImagesTask;
public synchronized void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Log.d("Image", "Loaded images with loader: " + data.getCount());
if (mLoadedImagesTask != null) {
mLoadedImagesTask.cancel(true);
}
mLoadedImagesTask = new AsyncTask<Cursor, Void, Map<String, Image>>() {
@Override
public Map<String, Image> doInBackground(Cursor... toLoads) {
Cursor toLoad = toLoads[0];
toLoad.moveToFirst();
Map<String, Image> map = new HashMap<>(toLoad.getCount());
while (!isCancelled() && !toLoad.isAfterLast()) {
Image curr = null;
synchronized (RiddleFragment.this) {
if (!isCancelled() && !toLoad.isClosed()) {
curr = Image.loadFromCursor(getActivity().getApplicationContext(), toLoad);
}
}
if (!isCancelled()) {
if (curr != null) {
map.put(curr.getHash(), curr);
}
toLoad.moveToNext();
}
}
if (isCancelled()) {
Log.e("Riddle", "Cancelled loaded images task, currently in map: " + map.size());
}
return map;
}
@Override
public void onPostExecute(Map<String, Image> result) {
mLoadedImagesTask = null;
if (result != null) {
ALL_IMAGES.clear();
ALL_IMAGES.putAll(result);
}
updateMenuButtons();
nextRiddleIfEmpty();
}
}.execute(data);
}
@Override
public synchronized void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
if (mLoadedImagesTask != null) {
Log.d("Riddle", "On loader reset cancels loaded images task.");
mLoadedImagesTask.cancel(true);
}
}
public void onWindowFocusChange(boolean hasFocus) {
if (mRiddleView != null) {
if (hasFocus) {
mRiddleView.onResume();
} else {
mRiddleView.onPause();
}
}
if (mBtnRiddles != null) {
updateMenuButtons();
}
}
private void handleUnclaimedAchievementsCountChanged(final int unclaimed) {
mMainHandler.post(new Runnable() {
@Override
public void run() {
if (unclaimed == 0) {
mOpenStore.setImageResource(R.drawable.alien_menu_enter_no_treasure);
} else {
mOpenStore.setImageResource(R.drawable.alien_menu_enter);
}
}
});
}
}