package com.ihunda.android.binauralbeat;
/*
* @author Giorgio Regni
* @contact @GiorgioRegni on Twitter
* http://twitter.com/GiorgioRegni
*
* This file is part of Binaural Beats Therapy or BBT.
*
* BBT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* BBT 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 BBT. If not, see <http://www.gnu.org/licenses/>.
*
* BBT project home is at https://github.com/GiorgioRegni/Binaural-Beats
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import com.facebook.FacebookSdk;
import com.facebook.appevents.AppEventsLogger;
import com.facebook.share.model.ShareLinkContent;
import com.facebook.share.widget.ShareDialog;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import com.ihunda.android.binauralbeat.viz.Black;
import com.ihunda.android.binauralbeat.viz.GLBlack;
import com.jjoe64.graphview.GraphView.GraphViewData;
import com.jjoe64.graphview.GraphView.GraphViewSeries;
import com.jjoe64.graphview.LineGraphView;
public class BBeat extends Activity {
enum eState {START, RUNNING, END};
enum appState {NONE, SETUP, INPROGRAM};
private static final int MAX_STREAMS = 5;
public static final float W_DELTA_FREQ = 2.00f;
public static final float W_THETA_FREQ = 6.00f;
public static final float W_ALPHA_FREQ = 10.00f;
public static final float W_BETA_FREQ = 20.00f;
public static final float W_GAMMA_FREQ = 60.00f;
public static final float W_MAX_BEAT = 80.00f;
private static final long ANIM_TIME_MS = 600;
private LinearLayout mPresetView;
private LinearLayout mInProgram;
private View mVizV;
private FrameLayout mVizHolder;
private TextView mStatus;
private ExpandableListView mPresetList;
private ToggleButton mPlayPause;
private appState state;
private int soundWhiteNoise;
private int soundUnity;
private SoundPool mSoundPool;
private NotificationManager mNotificationManager;
private static final int NOTIFICATION_STARTED = 1;
private PowerManager mPm;
private PowerManager.WakeLock mWl;
private Handler mHandler = new Handler();
private RunProgram programFSM;
private long pause_time = -1;
private Vector<StreamVoice> playingStreams;
private int playingBackground = -1;
private SeekBar soundBeatV;
private float mSoundBeatVolume;
private SeekBar soundBGV;
private float mSoundBGVolume;
private static final String SOURCE_CODE_URL = "http://bit.ly/BBeats";
private static final String BLOG_URL = "http://bit.ly/BBeatsBlog";
private static final String HELP_URL = "http://bit.ly/BBeatsHelp";
private static final String FORUM_URL = "https://plus.google.com/u/1/communities/113832254482827107359";
private static final String FACEBOOK_URL = "http://www.facebook.com/pages/Binaural-Beat-Therapy/121737064536801";
private static final String CONTACT_EMAIL = "binaural-beats@ihunda.com";
private static final String FACEBOOK_INSTALL_URL = "http://bit.ly/BBTFBSHARE";
private static final String FACEBOOK_SHARE_IMG = "http://i.imgur.com/bG9coHF.png";
private static final String LOGBBEAT = "BBT-MAIN";
private static final int NUM_START_BEFORE_DONATE = 1;
/* All dialogs declaration go here */
private static final int DIALOG_WELCOME = 1;
private static final int DIALOG_CONFIRM_RESET = 2;
private static final int DIALOG_GETTING_INVOLVED = 3;
private static final int DIALOG_JOIN_COMMUNITY = 4;
private static final int DIALOG_PROGRAM_PREVIEW = 5;
private static final int DIALOG_DONATE= 6;
private static final float DEFAULT_VOLUME = 0.6f;
private static final float BG_VOLUME_RATIO = 0.4f;
private static final float FADE_INOUT_PERIOD = 5f;
private static final float FADE_MIN = 0.6f;
private static final String PREFS_NAME = "BBT";
private static final String PREFS_VIZ = "VIZ";
private static final String PREFS_NUM_STARTS = "NUM_STARTS";
private VoicesPlayer vp;
boolean glMode = false;
boolean vizEnabled = true;
private LinearLayout mGraphVoicesLayout;
Map<String,ProgramMeta> programs;
ArrayList<CategoryGroup> groups;
private long numStarts;
// Stats tracking
/**
* Enum used to identify the tracker that needs to be used for tracking.
*
* A single tracker is usually enough for most purposes. In case you do need multiple trackers,
* storing them all in Application object helps ensure that they are created only once per
* application instance.
*/
public enum TrackerName {
APP_TRACKER, // Tracker used only in this app.
GLOBAL_TRACKER, // Tracker used by all the apps from a company. eg: roll-up tracking.
ECOMMERCE_TRACKER, // Tracker used by all ecommerce transactions from a company.
}
HashMap<TrackerName, Tracker> mTrackers = new HashMap<TrackerName, Tracker>();
public final String PROPERTY_ID = "UA-76238-16";
/*
* Not sure this is the best way to do it but it seems to work
* Some of the vizualisation need to get pointer to resources to load iamges, sounds, etc...
* */
private static BBeat instance;
/*
* Keeps a reference to a selected program only for the purpose of the
* preview dialog, should always be null when the preview dialog is not
* displayed
*/
private static Program _tmp_program_holder;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Facebook */
FacebookSdk.sdkInitialize(getApplicationContext());
setContentView(R.layout.main);
/* Init sounds */
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
/*
* Sets up power management, device should not go to sleep during a program
*/
mPm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWl = mPm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "BBTherapy");
/* Setup all buttons */
Button b = (Button) findViewById(R.id.MenuForum);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
gotoForum();
}
});
b = (Button) findViewById(R.id.MenuHelp);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
gotoHelp();
}
});
b = (Button) findViewById(R.id.Menu);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
openOptionsMenu();
}
});
b = (Button) findViewById((R.id.likeButton));
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
gotoFacebook();
}
});
b = (Button) findViewById((R.id.donateButton));
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
showDialog(DIALOG_DONATE);
}
});
TextView t = (TextView) findViewById((R.id.jointhecommunityText));
t.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
showDialog(DIALOG_JOIN_COMMUNITY);
}
});
mGraphVoicesLayout = (LinearLayout) findViewById(R.id.graphVoices);
pause_time = -1;
mPlayPause = (ToggleButton) findViewById((R.id.MenuPause));
mPlayPause.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
pauseOrResume();
}
});
/* Set up volume bar */
soundBeatV = (SeekBar) findViewById((R.id.soundVolumeBar));
soundBeatV.setMax(100);
mSoundBeatVolume = DEFAULT_VOLUME;
soundBeatV.setProgress((int) (mSoundBeatVolume*100));
soundBeatV.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
public void onStopTrackingTouch(SeekBar seekBar) {
}
public void onStartTrackingTouch(SeekBar seekBar) {
}
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mSoundBeatVolume = ((float) progress)/100.f;
resetAllVolumes();
}
});
/* Set up background volume bar */
soundBGV = (SeekBar) findViewById((R.id.soundBGVolumeBar));
soundBGV.setMax(100);
mSoundBGVolume = mSoundBeatVolume * BG_VOLUME_RATIO;
soundBGV.setProgress((int) (mSoundBGVolume*100));
soundBGV.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
public void onStopTrackingTouch(SeekBar seekBar) {
}
public void onStartTrackingTouch(SeekBar seekBar) {
}
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mSoundBGVolume = ((float) progress)/100.f;
resetAllVolumes();
}
});
mInProgram = (LinearLayout) findViewById(R.id.inProgramLayout);
mPresetView = (LinearLayout) findViewById(R.id.presetLayout);
mVizHolder = (FrameLayout) findViewById(R.id.VisualizationView);
mStatus = (TextView) findViewById(R.id.Status);
// Set a static pointer to this instance so that vizualisation can access it
setInstance(this);
/* Init Tracker */
Tracker tra = getTracker(TrackerName.APP_TRACKER);
// Enable Advertising Features.
tra.enableAdvertisingIdCollection(true);
mPresetList = (ExpandableListView) findViewById(R.id.presetListView);
//final List<String> programs = new ArrayList<String>(DefaultProgramsBuilder.getProgramMethods(this).keySet());
programs = DefaultProgramsBuilder.getProgramMethods(this);
groups = new ArrayList<CategoryGroup>();
for (String pname: programs.keySet()) {
ProgramMeta pm = programs.get(pname);
String catname = pm.getCat().toString();
CategoryGroup g = null;
/* Check if I already have a group with that name */
for (CategoryGroup g2: groups) {
if (g2.getName().equals(catname)) {
g = g2;
}
}
if (g == null) {
g = new CategoryGroup(catname);
try {
g.setNiceName(getString(R.string.class.getField("group_"+catname.toLowerCase()).getInt(null)));
} catch (Exception e) {
// pass
}
groups.add(g);
}
g.add(pm);
}
ProgramListAdapter adapter = new ProgramListAdapter(this, groups);
mPresetList.setOnGroupClickListener(new OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
/* Do nothing for now */
return false;
}
});
mPresetList.setOnChildClickListener(new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
selectProgram(groups.get(groupPosition).getObjets().get(childPosition));
return true;
}
});
LayoutInflater inflater = getLayoutInflater();
View headerView = inflater.inflate(R.layout.presetlist_getting_involved, null);
mPresetList.addHeaderView(headerView);
headerView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDialog(DIALOG_GETTING_INVOLVED);
}
});
mPresetList.setGroupIndicator(getResources().getDrawable(R.drawable.empty));
mPresetList.setAdapter(adapter);
// Expand all
for (int groupPosition =0; groupPosition < adapter.getGroupCount(); groupPosition++)
if (mPresetList.isGroupExpanded(groupPosition) == false) {
mPresetList.expandGroup(groupPosition);
}
showDialog(DIALOG_WELCOME);
_load_config();
initSounds();
state = appState.NONE;
goToState(appState.SETUP);
}
/*
* Takes the current setup and saves it to preference for next start
*/
private void _save_config() {
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean(PREFS_VIZ, vizEnabled);
editor.putLong(PREFS_NUM_STARTS, numStarts);
editor.commit();
}
private void _load_config() {
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
vizEnabled = settings.getBoolean(PREFS_VIZ, true);
numStarts = settings.getLong(PREFS_NUM_STARTS, 0);
}
void initSounds() {
if (mSoundPool != null) {
mSoundPool.release();
mSoundPool = null;
}
mSoundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);
soundWhiteNoise = mSoundPool.load(this, R.raw.whitenoise, 1);
soundUnity = mSoundPool.load(this, R.raw.unity, 1);
playingStreams = new Vector<StreamVoice>(MAX_STREAMS);
playingBackground = -1;
}
void startVoicePlayer() {
if (vp == null) {
vp = new VoicesPlayer();
vp.start();
}
}
void stopVoicePlayer() {
try {
vp.shutdown();
} catch (Exception e) {
// ignore
}
vp = null;
}
@Override
protected void onStart() {
Log.v(LOGBBEAT, "onStart");
super.onStart();
}
@Override
protected void onStop() {
Log.v(LOGBBEAT, "onStop");
if (!isInProgram())
stopVoicePlayer();
super.onStop();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
// ask for confirmation during workout
if (state == appState.INPROGRAM) {
showDialog(DIALOG_CONFIRM_RESET);
return true;
}
}
return super.onKeyDown(keyCode, event);
}
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.inprogram, menu);
return true;
}
@Override public boolean onPrepareOptionsMenu(Menu menu) {
if (state != appState.INPROGRAM)
return false;
return true;
}
/* Handles item selections */
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.stop: {
showDialog(DIALOG_CONFIRM_RESET);
return true;
}
case R.id.togglegraphics:
setGraphicsEnabled(!vizEnabled);
break;
}
return false;
}
private void goToState(appState newState) {
switch(state) {
case NONE:
mInProgram.setVisibility(View.GONE);
mPresetView.setVisibility(View.GONE);
break;
case INPROGRAM:
if (mWl.isHeld())
mWl.release();
mVizHolder.removeAllViews();
mVizV = null;
runGoneAnimationOnView(mInProgram);
_cancel_all_notifications();
stopVoicePlayer();
/* Reinit all sounds */
initSounds();
/* Check if its time to show a donate dialog */
if (numStarts % NUM_START_BEFORE_DONATE == NUM_START_BEFORE_DONATE-1)
showDialog(DIALOG_DONATE);
break;
case SETUP:
runGoneAnimationOnView(mPresetView);
break;
default:
break;
}
state = newState;
switch(state) {
case SETUP:
_track_screen("SETUP");
runComeBackAnimationOnView(mPresetView);
mPresetList.invalidate();
mVizHolder.setVisibility(View.GONE);
break;
case INPROGRAM:
_track_screen("INPROGRAM");
// Track number of usage
numStarts++;
_save_config();
// Start voice player thread
startVoicePlayer();
// Acquire power management lock
if (vizEnabled)
mWl.acquire();
_start_notification(programFSM.getProgram().getName());
runComeBackAnimationOnView(mInProgram);
mVizHolder.setVisibility(View.VISIBLE);
glMode = programFSM.pR.doesUseGL();
if (glMode)
mVizV = new GLVizualizationView(getBaseContext());
else
mVizV = new CanvasVizualizationView(getBaseContext());
mVizHolder.addView(mVizV);
mPlayPause.setChecked(true);
pause_time = -1;
break;
}
}
public boolean isInProgram() {
if (state == appState.INPROGRAM)
return true;
return false;
}
public boolean isPaused() {
if (pause_time > 0)
return true;
else
return false;
}
public void pauseOrResume() {
if (state == appState.INPROGRAM) {
if (pause_time > 0) {
long delta = _getClock() - pause_time;
programFSM.catchUpAfterPause(delta);
pause_time = -1;
unmuteAll();
} else {
/* This is a pause time */
pause_time = _getClock();
muteAll();
}
}
}
private void setGraphicsEnabled(boolean on) {
if (state == appState.INPROGRAM) {
if (vizEnabled && on == false) {
// Disable Viz
Period p = programFSM.getCurrentPeriod();
Visualization v;
if (((Object) mVizV).getClass() == GLVizualizationView.class)
v = new GLBlack();
else
v = new Black();
((VizualisationView) mVizV).stopVisualization();
((VizualisationView) mVizV).startVisualization(v, p.getLength());
((VizualisationView) mVizV).setFrequency(p.getVoices().get(0).freqStart);
vizEnabled = false;
if (mWl.isHeld())
mWl.release();
ToastText(R.string.graphics_off);
} else
if (!vizEnabled && on == true) {
// Enable viz
Period p = programFSM.getCurrentPeriod();
((VizualisationView) mVizV).stopVisualization();
((VizualisationView) mVizV).startVisualization(p.getV(), p.getLength());
((VizualisationView) mVizV).setFrequency(p.getVoices().get(0).freqStart);
vizEnabled = true;
if (mWl.isHeld() == false)
mWl.acquire();
ToastText(R.string.graphics_on);
}
}
_save_config();
}
@Override
protected void onDestroy() {
panic();
_cancel_all_notifications();
super.onDestroy();
}
@Override
protected void onPause() {
Log.v(LOGBBEAT, "onPause");
super.onPause();
// Facebook
// Logs 'app deactivate' App Event.
AppEventsLogger.deactivateApp(this);
}
@Override
protected void onResume() {
Log.v(LOGBBEAT, "onResume");
super.onResume();
// Facebook
// Logs 'install' and 'app activate' App Events.
AppEventsLogger.activateApp(this);
}
@Override
protected Dialog onCreateDialog(int id) {
switch(id) {
case DIALOG_WELCOME: {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.welcome_text)
.setCancelable(true)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
return alert;
}
case DIALOG_CONFIRM_RESET: {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.confirm_reset)
.setCancelable(true)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
BBeat.this.stopProgram();
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
return alert;
}
case DIALOG_GETTING_INVOLVED: {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.getting_involved_dialog)
.setCancelable(true)
.setPositiveButton(R.string.contact, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
shareWith(getString(R.string.app_name), getString(R.string.share_text));
}
})
.setNegativeButton(R.string.rate_on_market, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
gotoMarket();
}
}).setNeutralButton(R.string.donate, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
showDialog(DIALOG_DONATE);
}
});
AlertDialog alert = builder.create();
return alert;
}
case DIALOG_JOIN_COMMUNITY: {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.getting_involved_dialog)
.setCancelable(true)
.setPositiveButton(R.string.contact, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
shareWith(getString(R.string.app_name), getString(R.string.share_text));
}
})
.setNegativeButton(R.string.blog, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
gotoBlog();
}
});
AlertDialog alert = builder.create();
return alert;
}
case DIALOG_PROGRAM_PREVIEW: {
Program p = _tmp_program_holder;
if (p == null)
return null;
int length = p.getLength();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(p.getName())
.setIcon(android.R.drawable.ic_dialog_info)
.setMessage(p.getDescription() + " " + p.getAuthor() +
String.format(" %sh%smin.",
formatTimeNumberwithLeadingZero(length/60/60),
formatTimeNumberwithLeadingZero((length/60)%60))
)
.setCancelable(true)
.setPositiveButton(R.string.start, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
StartPreviouslySelectedProgram();
removeDialog(DIALOG_PROGRAM_PREVIEW);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(DIALOG_PROGRAM_PREVIEW);
}
});
AlertDialog alert = builder.create();
return alert;
}
case DIALOG_DONATE: {
_track_ui_click("DONATE", "DIALOG");
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.donate_text)
.setCancelable(true)
.setPositiveButton(R.string.donate, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
donatePayPalOnClick();
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(DIALOG_DONATE);
}
}).setNeutralButton(R.string.share_facebook, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(DIALOG_DONATE);
displayFacebookShare();
}
});
AlertDialog alert = builder.create();
return alert;
}
}
return null;
};
private void panic() {
// Stop all sounds
for (StreamVoice v: playingStreams)
mSoundPool.stop(v.streamID);
playingStreams.clear();
if (vp != null)
vp.stopVoices();
}
private void _start_notification(String programName) {
Notification notification = new Notification(R.drawable.icon, getString(R.string.notif_started), 0); // JENLA
Context context = getApplicationContext();
CharSequence contentTitle = getString(R.string.notif_started);
CharSequence contentText = getString(R.string.notif_descr, programName);
Intent notificationIntent = this.getIntent(); //new Intent(this, hiit.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
mNotificationManager.notify(NOTIFICATION_STARTED, notification);
}
private void _cancel_all_notifications() {
mNotificationManager.cancelAll();
}
private void selectProgram(ProgramMeta pm) {
if (programFSM != null)
programFSM.stopProgram();
Program p = DefaultProgramsBuilder.getProgram(pm, this);
_tmp_program_holder = p;
_track_ui_click(p.getName(), "select");
showDialog(DIALOG_PROGRAM_PREVIEW);
}
private void StartPreviouslySelectedProgram() {
Program p = _tmp_program_holder;
_tmp_program_holder = null;
_track_ui_click(p.getName(), "start");
((TextView) findViewById(R.id.programName)).setText(p.getName());
programFSM = new RunProgram(p, mHandler);
goToState(appState.INPROGRAM);
}
private void stopProgram() {
if (programFSM != null) {
programFSM.stopProgram();
programFSM = null;
}
panic();
goToState(appState.SETUP);
}
int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) {
int id = mSoundPool.play(soundID, leftVolume * mSoundBeatVolume, rightVolume * mSoundBeatVolume,
priority, loop, rate);
/*
* Record all playing stream ids to be able to stop sound on pause/panic
*/
playingStreams.add(new StreamVoice(id, leftVolume, rightVolume, loop, rate));
if (playingStreams.size() > MAX_STREAMS) {
StreamVoice v = playingStreams.remove(0);
mSoundPool.stop(v.streamID);
}
return id;
}
void stop(int soundID) {
mSoundPool.stop(soundID);
playingStreams.removeElement(new Integer(soundID));
}
/**
* Loop through all playing voices and set regular volume back
*/
void resetAllVolumes() {
if (playingStreams != null &&
mSoundPool != null)
for (StreamVoice v: playingStreams) {
if (v.streamID == playingBackground)
mSoundPool.setVolume(v.streamID, v.leftVol * mSoundBGVolume, v.rightVol * mSoundBGVolume);
else
mSoundPool.setVolume(v.streamID, v.leftVol * mSoundBeatVolume, v.rightVol * mSoundBeatVolume);
}
if (vp != null)
vp.setVolume(mSoundBeatVolume);
}
/**
* Loop through all playing voices and lower volume to 0 but do not stop
*/
void muteAll() {
if (playingStreams != null &&
mSoundPool != null)
for (StreamVoice v: playingStreams) {
mSoundPool.setVolume(v.streamID, 0, 0);
}
vp.setVolume(0);
}
/**
* Loop through all playing voices and set volume back
*/
void unmuteAll() {
resetAllVolumes();
}
private void playBackgroundSample(SoundLoop background, float vol) {
switch(background) {
case WHITE_NOISE:
playingBackground = play(soundWhiteNoise, vol, vol, 2, -1, 1.0f);
break;
case UNITY:
playingBackground = play(soundUnity, vol, vol, 2, -1, 1.0f);
break;
case NONE:
playingBackground = -1;
break;
default:
playingBackground = -1;
break;
}
if (playingBackground != -1)
mSoundPool.setVolume(playingBackground, vol * mSoundBGVolume, vol * mSoundBGVolume);
}
private void stopBackgroundSample() {
if (playingBackground != -1)
stop(playingBackground);
playingBackground = -1;
}
/**
* Go through a list of voices and start playing them with their start frequency
* @param voices list of voices to play
*/
protected void playVoices(ArrayList<BinauralBeatVoice> voices) {
vp.playVoices(voices);
vp.setVolume(mSoundBeatVolume);
}
/**
* @param voices
* @param pos
* @param length
* @return beat frequency of first voice
*/
protected float skewVoices(ArrayList<BinauralBeatVoice> voices, float pos, float length, boolean doskew) {
int i = 0;
float res = -1;
float freqs[] = new float[voices.size()];
for (BinauralBeatVoice v: voices) {
float ratio = (v.freqEnd - v.freqStart) / length;
if (res == -1)
res = ratio*pos + v.freqStart; // Only set res for the first voice
freqs[i] = res;
i++;
}
if (doskew) {
float fade_period = Math.min(FADE_INOUT_PERIOD/2, length/2);
if (length < FADE_INOUT_PERIOD)
vp.setFade(1f);
else if (pos < fade_period)
vp.setFade(FADE_MIN + pos/fade_period*(1f-FADE_MIN));
else if (length - pos < fade_period) {
float fade = FADE_MIN + (length-pos)/fade_period*(1f-FADE_MIN);
vp.setFade(fade);
}
else
vp.setFade(1f);
vp.setFreqs(freqs);
}
return res;
}
/**
* Go through all currently running beat voices and stop them
*/
protected void stopAllVoices() {
vp.stopVoices();
}
class RunProgram implements Runnable {
private static final long TIMER_FSM_DELAY = 1000 / 20;
private static final int GRAPH_VOICE_VIEW_PAST = 60;
private static final int GRAPH_VOICE_SPAN = 600;
private static final int GRAPH_VOICE_UPDATE = 5;
private Program pR;
private Iterator<Period> periodsIterator;
private Period currentPeriod;
private long cT; // current Period start time
private long startTime;
private long programLength;
private String sProgramLength;
private String formatString;
private String format_INFO_TIMING_MIN_SEC;
private long oldDelta; // Utilized to reduce the amount of redraw for the program legend
private eState s;
private Handler h;
LineGraphView graphView;
private long _last_graph_update;
public RunProgram(Program pR, Handler h) {
this.pR = pR;
this.h = h;
programLength = pR.getLength();
sProgramLength = getString(R.string.time_format,
formatTimeNumberwithLeadingZero((int) programLength/60),
formatTimeNumberwithLeadingZero((int) programLength%60));
formatString = getString(R.string.info_timing);
format_INFO_TIMING_MIN_SEC = getString(R.string.time_format_min_sec);
startTime = _getClock();
oldDelta = -1;
_last_graph_update = 0;
s = eState.START;
h.postDelayed(this, TIMER_FSM_DELAY);
}
public Period getCurrentPeriod() {
return currentPeriod;
}
public void stopProgram() {
stopAllVoices();
endPeriod();
h.removeCallbacks(this);
}
private void startPeriod(Period p) {
if (vizEnabled)
((VizualisationView) mVizV).startVisualization(p.getV(), p.getLength());
else {
Visualization v;
if (((Object) mVizV).getClass() == GLVizualizationView.class)
v = new GLBlack();
else
v = new Black();
((VizualisationView) mVizV).startVisualization(v, p.getLength());
}
((VizualisationView) mVizV).setFrequency(p.getVoices().get(0).freqStart);
playVoices(p.voices);
vp.setFade(FADE_MIN);
playBackgroundSample(p.background, p.getBackgroundvol());
Log.v(LOGBBEAT, String.format("New Period - duration %d", p.length));
}
private void inPeriod(long now, Period p, float pos) {
long delta = (now - startTime) / 50; // Do not refresh too often
float freq = skewVoices(p.voices, pos, p.length, oldDelta != delta);
((VizualisationView) mVizV).setFrequency(freq);
((VizualisationView) mVizV).setProgress(pos);
if (oldDelta != delta) {
oldDelta = delta;
delta = delta/20; // Down to seconds
mStatus.setText(String.format(formatString,
freq,
formatTimeNumberwithLeadingZero((int) delta/60),
formatTimeNumberwithLeadingZero((int) delta%60)
)
+
sProgramLength
);
updatePeriodGraph((now - startTime) / 1000);
}
}
private void endPeriod() {
//stopAllVoices();
stopBackgroundSample();
((VizualisationView) mVizV).stopVisualization();
}
public void catchUpAfterPause(long delta) {
cT += delta;
}
public void run() {
long now = _getClock();
switch(s) {
case START:
s = eState.RUNNING;
periodsIterator = pR.getPeriodsIterator();
cT = now;
drawPeriodGraph();
nextPeriod();
break;
case RUNNING:
if (isPaused())
break;
float pos = (now - cT) / 1000f;
if (pos > currentPeriod.length) {
endPeriod();
// Current period is over
if (!periodsIterator.hasNext()) {
// Finished
s = eState.END;
} else {
// this is a new period
cT = now;
nextPeriod();
}
} else {
/**
* In the middle of current period, adjust each beat voice
*/
inPeriod(now, currentPeriod, pos);
}
break;
case END:
BBeat.this.stopProgram();
return;
}
h.postDelayed(this, TIMER_FSM_DELAY);
}
private void nextPeriod() {
currentPeriod = periodsIterator.next();
startPeriod(currentPeriod);
}
public Program getProgram() {
return pR;
}
private void updatePeriodGraph(long now) {
// update viewport
if (now >= _last_graph_update + GRAPH_VOICE_UPDATE ) {
int viewstart = 0;
_last_graph_update = now;
if (GRAPH_VOICE_SPAN < programLength)
viewstart = (int) Math.max(0, now-GRAPH_VOICE_VIEW_PAST);
int viewsize = GRAPH_VOICE_SPAN;
if (graphView != null) {
graphView.setDrawBackground(true);
graphView.setDrawBackgroundLimit(now);
graphView.setViewPort(viewstart, viewsize);
}
}
}
private void drawPeriodGraph() {
Iterator<Period> iP = pR.getPeriodsIterator();
int numPeriods = pR.getNumPeriods();
GraphViewData data[] = new GraphViewData[numPeriods*2];
int i = 0;
int cursor = 0;
double maxFreq = 0;
while (iP.hasNext()) {
Period cP = iP.next();
data[i++] = new GraphViewData(cursor+0.01, cP.getMainBeatStart());
cursor += cP.getLength();
data[i++] = new GraphViewData(cursor, cP.getMainBeatEnd());
maxFreq = Math.max(maxFreq, cP.getMainBeatStart());
maxFreq = Math.max(maxFreq, cP.getMainBeatEnd());
}
GraphViewSeries voiceSeries = new GraphViewSeries(data);
graphView = new LineGraphView(
BBeat.this // context
, "Beat frequency" // heading
) {
@Override
protected String formatLabel(double value, boolean isValueX) {
if (isValueX) {
return String.format(format_INFO_TIMING_MIN_SEC,
formatTimeNumberwithLeadingZero((int) value/60),
formatTimeNumberwithLeadingZero((int) value%60));
} else {
return String.format("%.1f", value);
}
} };
graphView.addSeries(voiceSeries); // data
int viewstart = 0;
int viewsize = (int) Math.min(programLength, GRAPH_VOICE_SPAN);
graphView.setManualYAxisBounds(((int) Math.ceil(maxFreq)), 0);
graphView.setViewPort(viewstart, viewsize);
graphView.setScrollable(true);
// optional - activate scaling / zooming
//graphView.setScalable(true);
graphView.setDrawBackground(false);
mGraphVoicesLayout.removeAllViews();
mGraphVoicesLayout.addView(graphView);
}
}
public Animation runGoneAnimationOnView(View target) {
Animation animation = AnimationUtils.loadAnimation(this,
android.R.anim.slide_out_right );
animation.setDuration(ANIM_TIME_MS);
final View mTarget = target;
animation.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation) {
mTarget.setVisibility(View.GONE);
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
});
target.startAnimation(animation);
return animation;
}
public Animation runComeBackAnimationOnView(View target) {
Animation animation = AnimationUtils.loadAnimation(this,
android.R.anim.slide_in_left );
animation.setDuration(ANIM_TIME_MS);
final View mTarget = target;
mTarget.setVisibility(View.VISIBLE); // to be compatible with Android 1.5
animation.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation) {
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
});
target.startAnimation(animation);
return animation;
}
private String formatTimeNumberwithLeadingZero(int t) {
if (t > 9)
return String.format("%2d", t);
else
return String.format("0%1d", t);
}
private void gotoBlog() {
gotoURL(BLOG_URL);
}
@SuppressWarnings("unused")
private void gotoSourceCode() {
gotoURL(SOURCE_CODE_URL);
}
private void gotoForum() {
gotoURL(FORUM_URL);
}
private void gotoFacebook() {
gotoURL(FACEBOOK_URL);
}
private void gotoHelp() {
/*Intent i = new Intent(this, Comments.class);
i.putExtra("ID", "yoyoma");
startActivity(i);*/
gotoURL(HELP_URL);
}
private void gotoMarket() {
gotoURL("market://details?id=com.ihunda.android.binauralbeat");
}
private void gotoURL(String URL) {
try {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(URL));
startActivity(i);
}
catch(Exception e) {
}
}
public void shareWith(String subject,String text) {
final Intent intent = new Intent(Intent.ACTION_SEND);
String aEmailList[] = {CONTACT_EMAIL};
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_EMAIL, aEmailList);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, text);
startActivity(Intent.createChooser(intent, getString(R.string.share)));
}
private static void setInstance(BBeat instance) {
BBeat.instance = instance;
}
public static BBeat getInstance() {
return instance;
}
public String readRawTextFile(int resId)
{
InputStream inputStream = this.getResources().openRawResource(resId);
InputStreamReader inputreader = new InputStreamReader(inputStream);
BufferedReader buffreader = new BufferedReader(inputreader);
String line;
StringBuilder text = new StringBuilder();
try {
while (( line = buffreader.readLine()) != null) {
text.append(line);
text.append('\n');
}
} catch (IOException e) {
return null;
}
return text.toString();
}
private void ToastText(int id) {
Toast.makeText(this, getString(id), Toast.LENGTH_SHORT).show();
}
public class DonationsConfiguration {
public static final String TAG = "Donations";
public static final boolean DEBUG = false;
/** PayPal */
public static final String PAYPAL_USER = "giorgio.paypal@ihunda.com";
public static final String PAYPAL_ITEM_NAME = "Binaural Beats Therapy Donation";
public static final String PAYPAL_CURRENCY_CODE = "USD";
}
/**
* Donate button with PayPal by opening browser with defined URL
* For possible parameters see:
* https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_Appx_websitestandard_htmlvariables
*
* @param view
*/
public void donatePayPalOnClick() {
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.scheme("https").authority("www.paypal.com").path("cgi-bin/webscr");
uriBuilder.appendQueryParameter("cmd", "_donations");
uriBuilder.appendQueryParameter("business", DonationsConfiguration.PAYPAL_USER);
uriBuilder.appendQueryParameter("lc", "US");
uriBuilder.appendQueryParameter("item_name", DonationsConfiguration.PAYPAL_ITEM_NAME);
uriBuilder.appendQueryParameter("no_note", "0");
uriBuilder.appendQueryParameter("cn", "Message to Developer");
uriBuilder.appendQueryParameter("no_shipping", "1");
uriBuilder.appendQueryParameter("currency_code",
DonationsConfiguration.PAYPAL_CURRENCY_CODE);
// uriBuilder.appendQueryParameter("bn", "PP-DonationsBF:btn_donate_LG.gif:NonHosted");
Uri payPalUri = uriBuilder.build();
if (DonationsConfiguration.DEBUG) {
Log.d(DonationsConfiguration.TAG,
"Opening the browser with the url: " + payPalUri.toString());
}
// Start your favorite browser
Intent viewIntent = new Intent(Intent.ACTION_VIEW, payPalUri);
startActivity(viewIntent);
}
synchronized Tracker getTracker(TrackerName trackerId) {
if (!mTrackers.containsKey(trackerId)) {
GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);
Tracker t = (trackerId == TrackerName.APP_TRACKER) ? analytics.newTracker(PROPERTY_ID) : null;
/*: (trackerId == TrackerName.GLOBAL_TRACKER) ? analytics.newTracker(R.xml.global_tracker)
: analytics.newTracker(R.xml.ecommerce_tracker);*/
mTrackers.put(trackerId, t);
}
return mTrackers.get(trackerId);
}
private void _track_ui_click(String what, String cat) {
Tracker t = getTracker(TrackerName.APP_TRACKER);
if (t == null)
return;
t.send(new HitBuilders.EventBuilder()
.setCategory(cat)
.setAction(what)
.setLabel("click")
.setValue(1)
.build());
}
private void _track_ui_click(String what) {
_track_ui_click(what, "UI");
}
private void _track_time(String what, long timems) {
Tracker t = getTracker(TrackerName.APP_TRACKER);
if (t == null)
return;
// Build and send timing.
t.send(new HitBuilders.TimingBuilder()
.setCategory("Time")
.setValue(timems)
.setVariable(what)
.setLabel("ms")
.build());
}
private void _track_screen(String screenName) {
Tracker t = getTracker(TrackerName.APP_TRACKER);
if (t == null)
return;
// Set screen name.
t.setScreenName(screenName);
// Send a screen view.
t.send(new HitBuilders.AppViewBuilder().build());
}
private void ToastText(String s) {
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
private boolean displayFacebookShare() {
if (ShareDialog.canShow(ShareLinkContent.class)) {
ShareDialog shareDialog = new ShareDialog(this);
ShareLinkContent linkContent = new ShareLinkContent.Builder()
.setContentTitle("Binaural Beats Therapy App")
.setContentDescription("Already approved by hundred thousands of people, a very powerful self-improvement, brain enhancement and stress-relief app.")
.setContentUrl(Uri.parse(FACEBOOK_INSTALL_URL))
.setImageUrl(Uri.parse(FACEBOOK_SHARE_IMG))
.build();
shareDialog.show(linkContent);
ToastText("Opening facebook dialog...");
_track_ui_click("FACEBOOK_SHARE_OK");
return true;
}
// If the Facebook app is installed and we can present the share dialog
else {
ToastText("Couldn't open facebook dialog...");
_track_ui_click("FACEBOOK_SHARE_NOK");
return false;
}
}
private long _getClock() {
return SystemClock.elapsedRealtime();
}
}