
В качестве пробы пера я написал небольшую программу, которая выключает звук на ночь. Под катом описание процесса разработки и ссылка на программу.
Техзадание
Проблема заключается в том, что современные телефоны умеют гораздо больше, чем просто звонить. Они могут получать обновления с твиттера, электронной почты и кучи других мест. При этом они норовят сообщить пользователю об этих обновлениях. И вроде бы все неплохо до тех пор, пока не наступит ночь. Ложишься спать, закрываешь глаза, а тут «дзинь» или «ЖЖЖ-ЖЖЖ». В какой-то момент просто выключаешь звук и вибрацию для того, чтобы заснуть.
Как раз для этого и нужна программа. Она отключает все оповещения в заданный отрезок времени.
Среда разработки
Eclipse — практически единственное полноценное средство для разработки под Android. Для работы я выбрал Eclipse Classic 3.5.1.
Также вам понадобится Android SDK. Его можно найти на странице загрузки. Там же есть краткое руководство по установке.
С чего начать
Для меня разработка под Java и Android — совершенно новое занятие. Поэтому первым делом я внимательно изучил уроки, которые представлены в разделе Resources.
Также в разделе References вы найдете объемный справочник как по Andriod SDK, так и по стандартным java-пакетам. Единственное чего там не хватает, так это примеров использования. В результате сперва находишь нужный класс, а потом с помощью Google ищешь то, как им пользоваться в контексте приложения.
Интерфейс пользователя

Разработка интерфейса пользователя происходит во встроенном редакторе и понятна каждому, кто сталкивался с конструкторами форм:

Советы по проектированию интерфейсов для Android можно прочесть в специальном разделе Dev Guide. Там же находятся рекомендации по проектированию иконок:

Мне с трудом удалось вписать свой прямоугольник в перспективу, требуемую для иконок. Как делать что-то более сложное я не представляю. Тем не менее там есть набор шаблонов для Photoshop и Illustrator, который значительно облегчает создание иконок.
Хранение пользовательских настроек
Для работы с пользовательскими настройками в Android существует класс
PreferenceManager
. Метод getSharedPreferences()
принимает в качестве первого аргумента имя набора настроек, а в качестве второго — режим создания (закрытый, открытый для чтения или открытый для записи). В ответ мы получаем объект класса SharedPreferences
, который позволяет записывать и считывать настройки. Если такого набора нет, то он будет создан:SharedPreferences preferences = getSharedPreferences("goodnight", MODE_PRIVATE);
Для чтения различных типов данных специальные методы. Первый аргумент — название поля, а второй — значение по умолчанию:
preferences.getBoolean("isEnabled", false)
preferences.getInt("startHour", 23)
Для записи необходимо получить объект
SharedPreferences.Editor
с помощью метода edit()
, а в конце вызвать метод commit()
:Editor prefEditor =preferences.edit();
prefEditor.putBoolean("isEnabled",isEnabledCheckBox.isChecked());
prefEditor.putInt("startHour", startTimePicker.getCurrentHour());
prefEditor.putInt("startMinute", startTimePicker.getCurrentMinute());
prefEditor.putInt("endHour", endTimePicker.getCurrentHour());
prefEditor.putInt("endMinute", endTimePicker.getCurrentMinute());
prefEditor.commit();
Сервисы
Для того, чтобы не держать программу постоянно открытой и при этом что-то делать, в Android реализованы полноценные сервисы, которые могут работать в фоновом режиме. Разработчики iPhone об этом только мечтают.
Для создания сервиса необходимо создать собственный класс, унаследованный от
android.app.Service
и переопределяющий метод onBind()
:public class GoodnightService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
Также могут пригодиться методы
onCreate()
и onDestroy()
:@Override
public void onCreate() {
super.onCreate();
Log.i("GoodnightService", "Service created");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("GoodnightService", "Service destroyed");
}
Существует два типа взаимодействия с сервисами: передача параметров при старте или использование Android IDL (AIDL). В первом случае вы запускаете сервис и после этого вы можете только закрыть его. Во втором случае вы можете создать интерфейс, с помощью которого будет возможно управлять сервисом. Т.к. я планировал менять время начала и окончания ночи, то я выбрал второй вариант.
Для этого необходимо создать файл с расширением .aidl и в нем прописать интерфейс:
interface IGoodnightService {
void UpdateSettings();
}
Теперь необходимо скомпилировать проект для того, чтобы сгенерировать из файла .aidl IDL интерфейс. После компиляции можно обновить метод
onBind()
и создать новое свойство binder
: @Override
public IBinder onBind(Intent intent) {
return binder;
}
private final IGoodnightService.Stub binder = new IGoodnightService.Stub() {
@Override
public void UpdateSettings() {
Init();
Update();
}
};
Если у вас нет метода
Stub()
, то скорее всего файл с интерфейсом имеет расширение .java, а не .aidl.Еще необходимо добавить ваш сервис в манифест приложения AndroidManifest.xml:

Сервисы на стороне клиента
Чтобы использовать созданный интерфейс, при нажатии на кнопку «Применить», необходимо выполнить несколько простых действий. Во-первых нам понадобится объект с типом нашего интерфейса:
private IGoodnightService service;
Во-вторых, необходимо создать своиство типа
ServiceConnection
, которое отслеживает состояние сервиса и необходимо для связывания с ним интерфейса:private ServiceConnection svcConn=new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder binder) {
service=IGoodnightService.Stub.asInterface(binder);
}
public void onServiceDisconnected(ComponentName className) {
service=null;
}
};
И, наконец, нужно связать сервис с нашим приложением с помощью метода
bindService()
:bindService(new Intent(this,GoodnightService.class), svcConn, BIND_AUTO_CREATE);
Теперь можно смело обращаться к методам, описанным в
IGoodnightService
:try {
service.UpdateTimers();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Оповещения
Для того, чтобы хоть как-то показать работоспособность сервиса я воспользовался механизмом оповещений. Это может быть вибрация, звуковые сигналы или маленькая иконка в строке состояния (вторая слева):

Если открыть строку состояния, то можно увидеть оповещение в более развернутом виде:

Текстовые оповещения бывают двух видов: сообщающие о новых событиях (например, сообщение в твиттере :-)) или о том, что программа просто работает.
Оповещения первого типа можно удалить из списка, нажав на кнопку «Очистить уведомления». Сперва я делал их каждую минуту для того, чтобы они не пропадали. Практика показала, что спустя 5-6 часов все начинало дико тормозить, а оповещения больше не обновлялись. :-) После чего был найден второй способ.
Для создания оповещений нам понадобится
NotificationManager
, который можно получить с помощью метода getSystemService()
:final NotificationManager mgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Теперь нужно создать само оповещение (
Notification
):Notification note = new Notification(R.drawable.status,
getResources().getString(R.string.startMessage),
System.currentTimeMillis());
note.flags |= Notification.FLAG_ONGOING_EVENT;
PendingIntent i = PendingIntent.getActivity(this, 0,
new Intent(this, Setup.class), 0);
String interval = String.format("%d:%02d – %d:%02d", startHour,
startMinute, endHour, endMinute);
note.setLatestEventInfo(this, interval,
getResources().getString(R.string.notificationMessage), i);
Здесь
PendingIntent
— это ссылка на приложение, которое будет запущено после щелчка по уведомлению. А Notification.FLAG_ONGOING_EVENT
задает второй тип оповещений.Для отправки оповещения используется метод
notify()
:mgr.notify(NOTIFY_ME_ID, note);
Для того, чтобы удалить оповещение из строки состояния нужно вызвать метод
cancel()
:mgr.cancel(NOTIFY_ME_ID);
Выключение звука
Для того, чтобы выключить или включить звук, нужно получить
AudioManager
:AudioManager manager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
И установить соответствующий режим с помощью
setRingerMode()
:manager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
Финальная сборка приложения
Для того, чтобы собрать приложение в работающий apk-контейнер, нужно выбрать проект, из контекстного меню запустить мастер экспорта, расположенный в Android Tools, и сделать все, что он просит:


Подробнее об этом написано в разделе Publishing.
Микро-заключение
Писать приложения для Android не так просто, как расширения для Chrome. Тем не менее в этом нет ничего сверхъестественно сложного. :-) Главное начать.
Ссылка на приложение
Бонус для тех, кто дочитал:

http://x.product-studio.ru/Goodnight.apk
Буду рад услышать критику, пожелания и вопросы.