/*
* 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.achievement;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;
import java.util.HashSet;
import java.util.Set;
import dan.dit.whatsthat.util.compaction.Compacter;
import dan.dit.whatsthat.util.general.ObserverController;
/**
* An achievement is managed by this manager class which saves relevant achievement's data
* to permanent storage and loads previously saved content. The manager must be initialized
* with the application context before being able to retrieve the singleton. The manager must
* be told to commit changed data before the application context is closed or saving is not disturbing.
* Created by daniel on 12.05.15.
*/
public class AchievementManager implements AchievementDataEventListener {
private static final String ACHIEVEMENT_PREFERENCES_FILE = "dan.dit.whatsthat.achievement_data";
public static final int CHANGED_PROGRESS = 0;
public static final int CHANGED_TO_COVERED = 1;
public static final int CHANGED_TO_DISCOVERED = 2;
public static final int CHANGED_TO_ACHIEVED_AND_UNCLAIMED = 3;
public static final int CHANGED_GOT_CLAIMED = 4;
public static final int CHANGED_TO_RESET = 5;
public static final int CHANGED_OTHER = 6;
private static AchievementManager INSTANCE = null;
private final SharedPreferences mPrefs;
private Set<AchievementData> mManagedChangedData;
private Set<Achievement> mChangedAchievements;
private final ObserverController<OnAchievementChangedListener, AchievementChangeEvent>
mAchievementChangedEventController = new ObserverController<>();
/**
* Event class for the observers of achievement state.
*/
public static class AchievementChangeEvent {
private final Achievement mAchievement;
private int mChangedHint;
AchievementChangeEvent(Achievement achievement) {
mAchievement = achievement;
if (mAchievement == null) {
throw new IllegalArgumentException("Null achievement givenf or change event.");
}
}
private void setChangedHint(int changedHint) {
mChangedHint = changedHint;
}
public Achievement getAchievement() {
return mAchievement;
}
public int getChangedHint() {
return mChangedHint;
}
}
public interface OnAchievementChangedListener extends ObserverController.Observer<AchievementChangeEvent> {
}
private AchievementManager(Context applicationContext) {
if (applicationContext == null) {
throw new IllegalArgumentException("No context given.");
}
applicationContext = applicationContext.getApplicationContext();
mPrefs = applicationContext.getSharedPreferences(ACHIEVEMENT_PREFERENCES_FILE, Context.MODE_PRIVATE);
mManagedChangedData = new HashSet<>();
mChangedAchievements = new HashSet<>();
}
/**
* Initializes the manager by the given application context. Future invocations to getInstance()
* will be valid and return the singleton.
* @param applicationContext The application context.
*/
public static void initialize(Context applicationContext) {
INSTANCE = new AchievementManager(applicationContext);
}
/**
* Returns the singleton if it was previously initialized. Else the state is illegal.
* @return The singleton.
*/
public static AchievementManager getInstance() {
if (INSTANCE == null) {
throw new IllegalStateException("AchievementManager not yet initialized!");
}
return INSTANCE;
}
/**
* Closes manager at the end to commit changes to data and achievements. Does nothing
* if no instance initialized.
*/
public static synchronized void commit() {
if (INSTANCE != null && (!INSTANCE.mManagedChangedData.isEmpty() || !INSTANCE.mChangedAchievements.isEmpty())) {
SharedPreferences.Editor editor = INSTANCE.mPrefs.edit();
for (AchievementData data : INSTANCE.mManagedChangedData) {
editor.putString(data.mName, data.compact());
}
for (Achievement achievement : INSTANCE.mChangedAchievements) {
achievement.addData(editor);
}
editor.apply();
Log.d("Achievement", "Commiting achievement manager"
+ " , saving " + INSTANCE.mChangedAchievements.size() + " changed " +
"achievements.");
INSTANCE.mManagedChangedData.clear();
INSTANCE.mChangedAchievements.clear();
}
}
public void addAchievementChangedListener(OnAchievementChangedListener listener) {
mAchievementChangedEventController.addObserver(listener);
}
public void removeAchievementChangedListener(OnAchievementChangedListener listener) {
mAchievementChangedEventController.removeObserver(listener);
}
/**
* An achievement notifies its manager that it changed its state or data.
* @param achievement The achievement that changed.
* @param changedHint The hint of what kind of change happened to the achievement.
*/
public void onChanged(final Achievement achievement, final int changedHint) {
if (achievement != null) {
mChangedAchievements.add(achievement);
AchievementChangeEvent event = achievement.getStateChangeEvent();
event.setChangedHint(changedHint);
mAchievementChangedEventController.notifyObservers(event);
}
}
/**
* The given AchievementData will in future be managed by this manager. This is handy
* for the AchievementData that cannot or does not want to manage its own permanent data.
* @param data The not null data to be managed.
*/
public void manageAchievementData(AchievementData data) {
// for achievement data that is not managed by the using instance but rather left alone in its existence somewhere
// this offers a general interface for saving and loading
data.addListener(this);
}
@Override
public void onDataEvent(AchievementDataEvent event) {
mManagedChangedData.add(event.getChangedData());
}
/**
* Loads the data event with the given name from permanent storage.
* @param name The name identifying the data.
* @return The compacted data to reconstruct the data event.
*/
public Compacter loadDataEvent(String name) {
if (TextUtils.isEmpty(name)) {
return null;
}
String data = mPrefs.getString(name, null);
if (data == null) {
return null;
}
return new Compacter(data);
}
/**
* Returns the shared preferences used to save and load managed data.
* @return The shared preferences.
*/
SharedPreferences getSharedPreferences() {
return mPrefs;
}
}