Merge pull request #646 from HabitRPG/sounds

Sounds
This commit is contained in:
negue 2016-11-07 18:59:52 +01:00 committed by GitHub
commit 7fed4b92de
16 changed files with 370 additions and 21 deletions

View file

@ -0,0 +1 @@
ALTER TABLE Preferences ADD COLUMN 'sound' varchar(50);

View file

@ -433,4 +433,7 @@ To start, which parts of your life do you want to improve?</string>
<string name="open_market">Open Market</string>
<string name="cds_description" formatted="false">Your Dailies will next reset the first time you use Habitica after %1$s. Make sure you have completed your Dailies before this time!</string>
<string name="AudioTheme_title">Audio Theme</string>
<string name="AudioTheme_summary">Change Habitica\'s Audio Theme</string>
</resources>

View file

@ -86,4 +86,24 @@
<item>zh_TW</item>
</string-array>
<string-array name="AudioThemes">
<item>Off</item>
<item>Daniel the Bard</item>
<item>Watts\' Theme</item>
<item>Gokul Theme</item>
<item>LuneFox\'s Theme</item>
<item>Rosstavo\'s Theme</item>
<item>Dewin\'s Theme</item>
</string-array>
<string-array name="AudioValues">
<item>off</item>
<item>danielTheBard</item>
<item>wattsTheme</item>
<item>gokulTheme</item>
<item>luneFoxTheme</item>
<item>rosstavoTheme</item>
<item>dewinTheme</item>
</string-array>
</resources>

View file

@ -67,14 +67,19 @@
android:entries="@array/weekdays"
android:entryValues="@array/weekdayValues"
android:summary="@string/pref_first_day_of_the_week_summary"
android:order="4"/>
android:order="5"/>
<ListPreference android:title="@string/Language_title"
android:key="language"
android:entries="@array/Language"
android:entryValues="@array/LanguageValues"
android:summary="@string/Language_summary"
android:order="4"/>
<ListPreference android:title="@string/AudioTheme_title"
android:key="audioTheme"
android:entries="@array/AudioThemes"
android:entryValues="@array/AudioValues"
android:summary="@string/AudioTheme_summary"
android:order="3"/>
<Preference android:title="@string/logout"
android:key="logout"
android:summary="@string/logout_description"

View file

@ -7,5 +7,5 @@ public class HabitDatabase {
public static final String NAME = "Habitica";
public static final int VERSION = 28;
public static final int VERSION = 29;
}

View file

@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.components;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.helpers.SoundManager;
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager;
import com.habitrpg.android.habitica.modules.ApiModule;
import com.habitrpg.android.habitica.modules.AppModule;
@ -181,4 +182,6 @@ public interface AppComponent {
void inject(HabitButtonWidgetProvider habitButtonWidgetProvider);
void inject(AvatarStatsWidgetProvider avatarStatsWidgetProvider);
void inject(SoundManager soundManager);
}

View file

@ -0,0 +1,75 @@
package com.habitrpg.android.habitica.helpers;
import android.media.AudioManager;
import android.media.MediaPlayer;
import java.io.File;
public class SoundFile {
private String theme;
private String fileName;
private File file;
private MediaPlayer mp;
private Boolean playerPrepared = false;
public SoundFile(String theme, String fileName){
this.theme = theme;
this.fileName = fileName;
mp = new MediaPlayer();
}
public String getTheme() {
return theme;
}
public String getFileName() {
return fileName;
}
public String getWebUrl(){
return "https://habitica.com/assets/audio/"+getTheme()+"/"+getFileName()+".mp3";
}
public String getFilePath() {
return getTheme()+"_"+getFileName()+".mp3";
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public void prepareMediaPlayer(){
if(playerPrepared) {
return;
}
String path = file.getPath();
try {
mp.setDataSource(path);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.prepare();
playerPrepared = true;
file = null;
}
catch(Exception e){
e.printStackTrace();
}
}
public void play(){
prepareMediaPlayer();
if(mp.isPlaying()) {
mp.stop();
}
mp.start();
}
}

View file

@ -0,0 +1,78 @@
package com.habitrpg.android.habitica.helpers;
import com.habitrpg.android.habitica.HabiticaApplication;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
import rx.Observable;
import rx.exceptions.OnErrorThrowable;
import rx.schedulers.Schedulers;
// based on http://stackoverflow.com/questions/29838565/downloading-files-using-okhttp-okio-and-rxjava
public class SoundFileLoader {
OkHttpClient client;
public SoundFileLoader(){
client = new OkHttpClient();
}
public Observable<List<SoundFile>> download(List<SoundFile> files) {
return Observable.from(files)
.flatMap(audioFile -> {
File file = new File(getFullAudioFilePath(audioFile));
if (file.exists()) {
// Important, or else the MediaPlayer can't access this file
file.setReadable(true, false);
audioFile.setFile(file);
return Observable.just(audioFile);
}
final Observable<SoundFile> fileObservable = Observable.create(sub -> {
Request request = new Request.Builder().url(audioFile.getWebUrl()).build();
Response response;
try {
response = client.newCall(request).execute();
if (!response.isSuccessful()) { throw new IOException(); }
} catch (IOException io) {
throw OnErrorThrowable.from(OnErrorThrowable.addValueAsLastCause(io, audioFile));
}
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
try {
BufferedSink sink = Okio.buffer(Okio.sink(file));
sink.writeAll(response.body().source());
sink.flush();
sink.close();
} catch (IOException io) {
throw OnErrorThrowable.from(OnErrorThrowable.addValueAsLastCause(io, audioFile));
}
file.setReadable(true, false);
audioFile.setFile(file);
sub.onNext(audioFile);
sub.onCompleted();
}
});
return fileObservable.subscribeOn(Schedulers.io());
}, 5)
.toList()
.map(ArrayList::new);
}
private String getExternalCacheDir() {
return HabiticaApplication.getInstance(HabiticaApplication.currentActivity).getExternalCacheDir().getPath();
}
public String getFullAudioFilePath(SoundFile soundFile) {
return getExternalCacheDir() + File.separator + soundFile.getFilePath();
}
}

View file

@ -0,0 +1,95 @@
package com.habitrpg.android.habitica.helpers;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.util.Log;
import com.habitrpg.android.habitica.HabiticaApplication;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.inject.Inject;
import rx.Observable;
import rx.schedulers.Schedulers;
public class SoundManager {
public static String SoundAchievementUnlocked = "Achievement_Unlocked";
public static String SoundChat = "Chat";
public static String SoundDaily = "Daily";
public static String SoundDeath = "Death";
public static String SoundItemDrop = "Item_Drop";
public static String SoundLevelUp = "Level_Up";
public static String SoundMinusHabit = "Minus_Habit";
public static String SoundPlusHabit = "Plus_Habit";
public static String SoundReward = "Reward";
public static String SoundTodo = "ToDo";
@Inject
SoundFileLoader soundFileLoader;
private String soundTheme;
private MediaPlayer mp = new MediaPlayer();
private HashMap<String, SoundFile> loadedSoundFiles;
public SoundManager(){
HabiticaApplication.getInstance(HabiticaApplication.currentActivity).getComponent().inject(this);
loadedSoundFiles = new HashMap<>();
}
public void setSoundTheme(String soundTheme){
this.soundTheme = soundTheme;
}
public Observable<List<SoundFile>> preloadAllFiles() {
if(soundTheme == "off") {
return Observable.empty();
}
ArrayList<SoundFile> soundFiles = new ArrayList<>();
soundFiles.add(new SoundFile(soundTheme, SoundAchievementUnlocked));
soundFiles.add(new SoundFile(soundTheme, SoundChat));
soundFiles.add(new SoundFile(soundTheme, SoundDaily));
soundFiles.add(new SoundFile(soundTheme, SoundDeath));
soundFiles.add(new SoundFile(soundTheme, SoundItemDrop));
soundFiles.add(new SoundFile(soundTheme, SoundLevelUp));
soundFiles.add(new SoundFile(soundTheme, SoundMinusHabit));
soundFiles.add(new SoundFile(soundTheme, SoundPlusHabit));
soundFiles.add(new SoundFile(soundTheme, SoundReward));
soundFiles.add(new SoundFile(soundTheme, SoundTodo));
return soundFileLoader.download(soundFiles);
}
public void clearLoadedFiles(){
loadedSoundFiles.clear();
}
public void loadAndPlayAudio(String type){
if(soundTheme == "off")
{
return;
}
if(loadedSoundFiles.containsKey(type)){
loadedSoundFiles.get(type).play();
} else {
ArrayList<SoundFile> soundFiles = new ArrayList<>();
soundFiles.add(new SoundFile(soundTheme, type));
soundFileLoader.download(soundFiles).observeOn(Schedulers.newThread()).subscribe(audioFiles1 -> {
SoundFile file = soundFiles.get(0);
loadedSoundFiles.put(type, file);
file.play();
}, throwable -> throwable.printStackTrace());
}
}
}

View file

@ -1,15 +1,16 @@
package com.habitrpg.android.habitica.modules;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.helpers.TagsHelper;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.support.v7.preference.PreferenceManager;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.helpers.SoundFileLoader;
import com.habitrpg.android.habitica.helpers.SoundManager;
import com.habitrpg.android.habitica.helpers.TagsHelper;
import javax.inject.Named;
import javax.inject.Singleton;
@ -53,4 +54,15 @@ public class AppModule {
public Resources providesResources(Context context) {
return context.getResources();
}
@Provides
public SoundFileLoader providesSoundFileLoader(){
return new SoundFileLoader();
}
@Provides
@Singleton
public SoundManager providesSoundManager() {
return new SoundManager();
}
}

View file

@ -13,6 +13,7 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
/**
* Created by keithholliday on 5/29/16.

View file

@ -4,14 +4,12 @@ import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDoneException;
import android.databinding.DataBindingUtil;
@ -21,6 +19,10 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -41,12 +43,13 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.amplitude.api.Amplitude;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.helpers.SoundFile;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.HostConfig;
import com.habitrpg.android.habitica.NotificationPublisher;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.helpers.SoundFileLoader;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
import com.habitrpg.android.habitica.callbacks.ItemsCallback;
import com.habitrpg.android.habitica.callbacks.MergeUserCallback;
@ -84,6 +87,7 @@ import com.habitrpg.android.habitica.events.commands.UnlockPathCommand;
import com.habitrpg.android.habitica.events.commands.UpdateUserCommand;
import com.habitrpg.android.habitica.helpers.AmplitudeManager;
import com.habitrpg.android.habitica.helpers.LanguageHelper;
import com.habitrpg.android.habitica.helpers.SoundManager;
import com.habitrpg.android.habitica.helpers.TaskAlarmManager;
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager;
import com.habitrpg.android.habitica.ui.AvatarView;
@ -143,8 +147,6 @@ import com.raizlabs.android.dbflow.structure.BaseModel;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.json.JSONException;
import org.json.JSONObject;
import org.solovyev.android.checkout.ActivityCheckout;
import org.solovyev.android.checkout.Checkout;
@ -157,7 +159,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@ -167,6 +168,7 @@ import butterknife.BindView;
import retrofit2.adapter.rxjava.HttpException;
import rx.Observable;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import static com.habitrpg.android.habitica.ui.helpers.UiUtils.SnackbarDisplayType;
import static com.habitrpg.android.habitica.ui.helpers.UiUtils.showSnackbar;
@ -179,6 +181,9 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
public static final int MIN_LEVEL_FOR_SKILLS = 11;
@Inject
public APIHelper apiHelper;
@Inject
public SoundManager soundManager;
@Inject
public MaintenanceApiService maintenanceService;
public HabitRPGUser user;
@ -255,6 +260,8 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
getResources().updateConfiguration(configuration,
getResources().getDisplayMetrics());
if (!HabiticaApplication.checkUserAuthentication(this, hostConfig)) {
return;
}
@ -382,6 +389,8 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
apiHelper.languageCode = preferences.getLanguage();
}
soundManager.setSoundTheme(preferences.getSound());
Calendar calendar = new GregorianCalendar();
TimeZone timeZone = calendar.getTimeZone();
long offset = -TimeUnit.MINUTES.convert(timeZone.getOffset(calendar.getTimeInMillis()), TimeUnit.MILLISECONDS);
@ -1002,6 +1011,7 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
} else {
snackbarMessage = getApplicationContext().getString(R.string.armoireExp);
}
soundManager.loadAndPlayAudio(SoundManager.SoundItemDrop);
} else if (!event.Reward.getId().equals("potion")) {
EventBus.getDefault().post(new TaskRemovedEvent(event.Reward.getId()));
}
@ -1031,6 +1041,7 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
}, throwable -> {
});
} else {
soundManager.loadAndPlayAudio(SoundManager.SoundReward);
// user created Rewards
apiHelper.apiService.postTaskDirection(rewardKey, TaskDirection.down.toString())
.compose(apiHelper.configureApiCallObserver())
@ -1205,7 +1216,10 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
private void showSnackBarForDataReceived(final TaskDirectionData data) {
if (data.get_tmp() != null) {
if (data.get_tmp().getDrop() != null) {
new Handler().postDelayed(() -> showSnackbar(MainActivity.this, floatingMenuWrapper, data.get_tmp().getDrop().getDialog(), SnackbarDisplayType.DROP), 3000L);
new Handler().postDelayed(() -> {
showSnackbar(MainActivity.this, floatingMenuWrapper, data.get_tmp().getDrop().getDialog(), SnackbarDisplayType.DROP);
soundManager.loadAndPlayAudio(SoundManager.SoundItemDrop);
}, 3000L);
}
}
}
@ -1284,12 +1298,14 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
})
.create();
soundManager.loadAndPlayAudio(SoundManager.SoundDeath);
this.faintDialog.show();
}
}
private void displayLevelUpDialog(int level) {
soundManager.loadAndPlayAudio(SoundManager.SoundLevelUp);
SuppressedModals suppressedModals = user.getPreferences().getSuppressModals();
if (suppressedModals != null) {
if (suppressedModals.getLevelUp()) {
@ -1447,6 +1463,15 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
.compose(apiHelper.configureApiCallObserver())
.subscribe(new TaskScoringCallback(this, event.Task.getId()), throwable -> {
});
switch(event.Task.type){
case Task.TYPE_DAILY: {
soundManager.loadAndPlayAudio(SoundManager.SoundDaily);
} break;
case Task.TYPE_TODO: {
soundManager.loadAndPlayAudio(SoundManager.SoundTodo);
} break;
}
}
@Subscribe
@ -1463,6 +1488,8 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
.compose(apiHelper.configureApiCallObserver())
.subscribe(new TaskScoringCallback(this, event.habit.getId()), throwable -> {
});
soundManager.loadAndPlayAudio(event.Up ? SoundManager.SoundPlusHabit : SoundManager.SoundMinusHabit);
}
@Subscribe

View file

@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.ui.fragments;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.helpers.SoundManager;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.raizlabs.android.dbflow.sql.builder.Condition;
@ -21,6 +22,9 @@ public abstract class BaseMainFragment extends BaseFragment {
@Inject
public APIHelper apiHelper;
@Inject
protected SoundManager soundManager;
public MainActivity activity;
public TabLayout tabLayout;
public FrameLayout floatingMenuWrapper;

View file

@ -1,7 +1,5 @@
package com.habitrpg.android.habitica.ui.fragments.preferences;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@ -14,10 +12,10 @@ import android.support.v7.preference.PreferenceScreen;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.NotificationPublisher;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.callbacks.MergeUserCallback;
import com.habitrpg.android.habitica.helpers.LanguageHelper;
import com.habitrpg.android.habitica.helpers.SoundManager;
import com.habitrpg.android.habitica.helpers.TaskAlarmManager;
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager;
import com.habitrpg.android.habitica.prefs.TimePreference;
@ -30,7 +28,6 @@ import com.raizlabs.android.dbflow.runtime.transaction.TransactionListener;
import com.raizlabs.android.dbflow.sql.builder.Condition;
import com.raizlabs.android.dbflow.sql.language.Select;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@ -42,6 +39,8 @@ public class PreferencesFragment extends BasePreferencesFragment implements
@Inject
public APIHelper apiHelper;
@Inject
public SoundManager soundManager;
private Context context;
private TimePreference timePreference;
private PreferenceScreen pushNotificationsPreference;
@ -224,6 +223,23 @@ public class PreferencesFragment extends BasePreferencesFragment implements
this.startActivity(intent);
getActivity().finishAffinity();
}
} else if (key.equals("audioTheme")) {
String newAudioTheme = sharedPreferences.getString(key, "off");
Map<String, Object> updateData = new HashMap<>();
updateData.put("preferences.sound", newAudioTheme);
MergeUserCallback mergeUserCallback = new MergeUserCallback(activity, user);
apiHelper.apiService.updateUser(updateData)
.compose(apiHelper.configureApiCallObserver())
.subscribe(mergeUserCallback, throwable -> {
});
Preferences preferences = user.getPreferences();
preferences.setSound(newAudioTheme);
soundManager.setSoundTheme(newAudioTheme);
soundManager.preloadAllFiles();
}
}

View file

@ -16,6 +16,7 @@ import com.habitrpg.android.habitica.events.commands.EditTagCommand;
import com.habitrpg.android.habitica.events.commands.FilterTasksByTagsCommand;
import com.habitrpg.android.habitica.events.commands.RefreshUserCommand;
import com.habitrpg.android.habitica.events.commands.UpdateTagCommand;
import com.habitrpg.android.habitica.helpers.SoundManager;
import com.habitrpg.android.habitica.helpers.TagsHelper;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity;

View file

@ -33,7 +33,7 @@ public class Preferences extends BaseModel {
@Column
private boolean costume, toolbarCollapsed, advancedCollapsed, tagsCollapsed, newTaskEdit, disableClasses, stickyHeader, sleep, hideHeader;
@Column
private String allocationMode, shirt, skin, size, background, chair, language;
private String allocationMode, shirt, skin, size, background, chair, language, sound;
@Column
private int dayStart, timezoneOffset;
@ -152,6 +152,14 @@ public class Preferences extends BaseModel {
this.size = size;
}
public String getSound() {
return sound;
}
public void setSound(String sound) {
this.sound = sound;
}
public int getTimezoneOffset() {
return timezoneOffset;
}