Проблемы и способы их решения




В ходе написания программы возникло некоторое количество проблем.

1. Организация поддержки татарского языка.

Языковые настройки являются частью конфигурации устройства - набор характеристик, описывающих текущее состояние конкретного устройства. Android предоставляет конфигурационные квалификаторы для разных языков, существенно упрощающие локализацию: достаточно создать подкаталоги ресурсов с разными языковыми квалификаторами и разместить в них альтернативные ресурсы. Система ресурсов Android делает все остальное. Языковые квалификаторы позаимствованы из кодов ISO 639-1. Существует квалификатор и для татарского языка – квалификатор -tt. На рис. 12 представлен каталог ресурсов приложения.

 

 

Рис. 12. Каталог ресурсов.

 

Далее, при изменении настроек языка нужно изменять локаль. За это отвечает код в классе Settings из листинга 1.

Листинг 1. Изменение конфигурации устройства при изменении настроек приложения (Settings.java)

public class Settings {

...

public enum LANGUAGE {

Russian, Tatar

};

...

public void setLanguage(LANGUAGE lang) {

mLanguage = lang;

setLocale();

saveSettings();

}

private void setLocale() {

String qualifier;

switch (mLanguage) {

case Russian:

qualifier = "ru";

break;

case Tatar:

qualifier = "tt";

break;

default:

qualifier = mDefaultQualifier;

break;

}

 

Locale locale = new Locale(qualifier);

Locale. setDefault (locale);

android.content.res.Configuration config = new android.content.res.Configuration();

config.locale = locale;

mAppContext.getResources().updateConfiguration(config,

mAppContext.getResources().getDisplayMetrics());

}

...

}

 

Вроде бы, проблема решена. Однако если повернуть устройство, тем самым изменив его конфигурацию, язык в приложении станет тем же, что и на устройстве. Это связано с тем, что Android уничтожает текущую активность и создает новую при каждом изменении конфигурации времени выполнения, чтобы обеспечить оптимальный подбор ресурсов для новой конфигурации. Одним из решений является переопределения метода жизненного цикла onCreate(…), добавив в метод изменение локали. Это кажется оптимальным решением, ведь в приложении всего две активности. Однако со временем при усовершенствовании приложения их может стать больше. И чтобы не добавлять один и тот же код в каждую из них, был создан класс LocaleSettingFragmentActivity, который расширяют все активности приложения (листинг 2).

Листинг 2. (LocaleSettingFragmentActivity.java)

public abstract class LocaleSettingFragmentActivity extends FragmentActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super. onCreate(savedInstanceState);

Settings. get (this).set();

}

}

 

В методе set() класса Settings вызывается метод setLocale() того же класса.

Наконец, так как диалоговое окно выбора языка вызывается из фрагмента TopicsFragment, необходимо каким-то образом обновить фрагмент после выхода из меню. Для этого фрагмент отсоединяется и присоединяется к менеджеру фрагментов активности, тем самым заставляя Android заново построить фрагмент (листинг 3).

Листинг 3. Обновление фрагмента (TopicsFragment.java)

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {

if (resultCode!= Activity. RESULT_OK)

return;

if (requestCode == REQUEST_LANGUAGE) {

getActivity().getSupportFragmentManager().beginTransaction()

.detach(this).attach(this).commit();

}

...

}

 

2. Запись собственного звука

Для записи звука с диктофона устройства использован экземпляр класса MediaRecorder. В листинге 4 представлены методы класса UserSoundFragment, отвечающие за начало и остановку записи.

Листинг 4. Запись звука (UserSoundFragment.java)

public class UserSoundFragment extends DialogFragment{

...

private boolean mRecordingStarted;

private MediaRecorder mRecorder;

private String mSoundFileName;

private static final int RECORDING_MAX_LENGTH_IN_SECONDS = 3;

...

private void startRecording() {

mRecordingStarted = true;

...

if (mSoundFileName == null) {

mSoundFileName = UUID. randomUUID ().toString() + ".3gp";

getArguments().putString(EXTRA_SOUND_FILENAME, mSoundFileName);

}

String soundFileAbsolutePath = Settings. get (getActivity())

.getApplicationExternalStorage() + "/" + mSoundFileName;

 

mRecorder = new MediaRecorder();

mRecorder.setAudioSource(MediaRecorder.AudioSource. MIC);

mRecorder.setOutputFormat(MediaRecorder.OutputFormat. THREE_GPP);

mRecorder.setOutputFile(soundFileAbsolutePath);

mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder. AMR_NB);

 

boolean preparationSuccess = true;

try {

mRecorder.prepare();

} catch (IOException e) {

Log. e (TAG, "recorder prepare() failed");

preparationSuccess = false;

}

if (preparationSuccess) {

 

Handler handler = new Handler();

handler.postDelayed(new Runnable() {

@Override

public void run() {

stopRecording();

}

}, RECORDING_MAX_LENGTH_IN_SECONDS * 1000);

 

mRecorder.start();

} else {

releaseRecorder();

stopRecording();

}

}

 

private void stopRecording() {

mRecordingStarted = false;

updateRecordButton();

if (mRecorder!= null) {

mRecorder.stop();

releaseRecorder();

}

...

}

private void releaseRecorder() {

if (mRecorder!= null) {

mRecorder. release ();

mRecorder = null;

}

}

}

Звук записывается в файл на внешней памяти. В листинге 5 продемонстрирован код, позволяющий сделать это.

 

Листинг 5. Доступ к внешней памяти (Settings.java)

public class Settings {

...

private static final String APPLICATION_FOLDER_NAME = "FlashCards";

...

public String getApplicationExternalStorage() {

File appFolder = new File(Environment. getExternalStorageDirectory ().getAbsolutePath()

+ "/" + APPLICATION_FOLDER_NAME);

appFolder.mkdir();

return appFolder.getAbsolutePath();

}

}

 

Чтоб получить доступ к диктофону и внешней памяти устройства, следует запросить для приложения право на их использование, добавив в манифест элемент uses-permission. Манифест (manifest) представляет собой файл XML с метаданными, описывающими приложение для ОС Android (рис. 13).

Рис. 13. Файл AndroidManifest.xml.

 

3. Сохранение настроек и записанных звуков

Каждое приложение на устройстве Android имеет каталог в своей песочнице (sandbox). Хранение файлов в песочнице защищает их от других приложений и даже любопытных глаз пользователей (если только устройство не было «взломано» — в этом случае пользователь сможет делать все, что ему заблагорассудится). Песочница каждого приложения представляет собой подкаталог каталога /data/data, имя которого соответствует имени пакета приложения.

Поддержка долгосрочного хранения данных в приложении включает два процесса: сохранение данных в файловой системе и их загрузка при запуске приложения.

Каждый процесс состоит из двух фаз. При сохранении данные сначала преобразуются в формат хранения, после чего результат записывается в файл. При загрузке все происходит наоборот: отформатированные данные сначала читаются из файла, а затем разбираются в формат, с которым работает приложение. Для приложения FlashCards форматом хранения является формат JSON, а операции чтения и записи файлов осуществляются методами ввода-вывода класса Android Context. Формат JSON (JavaScript Object Notation) стал популярным в последнее время, особенно в области веб-служб. Android включает стандартный пакет org.json, классы которого предоставляют средства для создания и разбора файлов в формате JSON.

Механика создания и разбора объектов модели в формате JSON делегирована классу FlashCardsJSONSerializer. В листинге 6 приведен процесс сохранения и загрузки настроек.

public class FlashCardsJSONSerializer {

 

private Context mContext;

 

public FlashCardsJSONSerializer(Context context) {

mContext = context;

}

 

public void saveSettings(String fileName) throws JSONException, IOException {

Writer writer = null;

try {

OutputStream out = mContext.openFileOutput(fileName,

Context. MODE_PRIVATE);

writer = new OutputStreamWriter(out);

writer.write(Settings. get (mContext).settingsToJSON().toString());

} finally {

if (writer!= null)

writer.close();

}

}

 

public JSONObject loadSettings(String fileName) throws IOException,

JSONException {

 

BufferedReader reader = null;

try {

InputStream in = mContext.openFileInput(fileName);

reader = new BufferedReader(new InputStreamReader(in));

StringBuilder jsonString = new StringBuilder();

String line = null;

while ((line = reader.readLine())!= null) {

jsonString.append(line);

}

JSONArray array = (JSONArray) new JSONTokener(jsonString.toString())

.nextValue();

return array.getJSONObject(0);

 

} catch (FileNotFoundException e) {

} finally {

if (reader!= null)

reader.close();

}

return null;

}

}

Далее необходимо добавить поддержку сериализации JSON в классе Settings (листинг 6).

Листинг 6. Реализация сериализации JSON(Settings.java)

public class Settings {

 

...

private FlashCardsJSONSerializer mSerializer;

 

private static final String SETTINGS_FILENAME = "settings.json";

 

private static final String JSON_LANGUAGE = "language";

private static final String JSON_SOUND_ON = "sound_on";

private static final String JSON_CARDTITLE_VISIBLE = "cardtitle_visible";

private static final String JSON_SLIDESHOW_INTERVAL = "slideshow_interval";

private static final String JSON_SLIDESHOW_FROMTHEBEGINNING = "slideshow_fromthebeginning";

 

public enum LANGUAGE {

Russian, Tatar

};

 

private LANGUAGE mLanguage;

private boolean mSoundOn;

private boolean mCardTitleVisible;

private int mSlideShowInterval; // in milliseconds

private int mMaxSlideShowInterval = 5000;

private int mMinSlideShowInterval = 500;

private boolean mSlideShowFromTheBeginning;

...

public JSONObject settingsToJSON() throws JSONException {

JSONObject json = new JSONObject();

json.put(JSON_LANGUAGE, mLanguage);

json.put(JSON_SOUND_ON, mSoundOn);

json.put(JSON_CARDTITLE_VISIBLE, mCardTitleVisible);

json.put(JSON_SLIDESHOW_INTERVAL, mSlideShowInterval);

json.put(JSON_SLIDESHOW_FROMTHEBEGINNING, mSlideShowFromTheBeginning);

return json;

}

...

}

 

Осталось добавить инициирование сохранения и загрузки данных в Settings (листинг 7).

Листинг 7. Инициирование сохранения и загрузки данных(Settings.java)

public class Settings {

 

private static Settings sSettings;

private Context mAppContext;

 

private static final String TAG = "Settings";

...

private FlashCardsJSONSerializer mSerializer;

 

private static final String SETTINGS_FILENAME = "settings.json";

 

private static final String JSON_LANGUAGE = "language";

private static final String JSON_SOUND_ON = "sound_on";

private static final String JSON_CARDTITLE_VISIBLE = "cardtitle_visible";

private static final String JSON_SLIDESHOW_INTERVAL = "slideshow_interval";

private static final String JSON_SLIDESHOW_FROMTHEBEGINNING = "slideshow_fromthebeginning";

 

public enum LANGUAGE {

Russian, Tatar

};

private LANGUAGE mLanguage;

private LANGUAGE mDefaultLanguage = LANGUAGE. Russian;

private String mDefaultQualifier = "ru";

 

private boolean mSoundOn;

private boolean mDefaultSoundOn = true;

 

private boolean mCardTitleVisible;

private boolean mDefaultCardTitleVisible = true;

 

private int mSlideShowInterval; // in millisec

private int mMaxSlideShowInterval = 5000;

private int mMinSlideShowInterval = 500;

private int mDefaultSlideShowInterval = 1000;

 

private boolean mSlideShowFromTheBeginning;

private boolean mDefaultSlideShowFromTheBeginning = false;

 

public static Settings get(Context c) {

if (sSettings == null) {

sSettings = new Settings(c.getApplicationContext());

}

return sSettings;

}

 

private Settings(Context c) {

mAppContext = c;

mSerializer = new FlashCardsJSONSerializer(mAppContext);

loadSettings();

}

 

public void set() {

setLocale();

}

 

private boolean saveSettings() {

try {

mSerializer.saveSettings(SETTINGS_FILENAME);

return true;

} catch (Exception e) {

Log. e (TAG, "Error saving settings: ", e);

return false;

}

}

 

private void loadSettings() {

setDefault();

try {

JSONObject json = mSerializer.loadSettings(SETTINGS_FILENAME);

mLanguage = LANGUAGE. valueOf (json.getString(JSON_LANGUAGE));

mCardTitleVisible = json.getBoolean(JSON_CARDTITLE_VISIBLE);

mSlideShowInterval = json.getInt(JSON_SLIDESHOW_INTERVAL);

mSoundOn = json.getBoolean(JSON_SOUND_ON);

mSlideShowFromTheBeginning = json

.getBoolean(JSON_SLIDESHOW_FROMTHEBEGINNING);

} catch (Exception e) {

Log. e (TAG, "Error loading settings: ", e);

}

}

 

private void setDefault() {

mLanguage = mDefaultLanguage;

mSlideShowInterval = mDefaultSlideShowInterval;

mSoundOn = mDefaultSoundOn;

mSlideShowFromTheBeginning = mDefaultSlideShowFromTheBeginning;

mCardTitleVisible = mDefaultCardTitleVisible;

}

...

}

Сохранение информации о записанных звуках реализовано абсолютно аналогично.


 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-08-08 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: