Облачный хостинг VDS за 2 минуты

Настоящий облачный VDS-хостинг от UltraVDS: тестируем производительность

Дельта-синхронизация крипто-дисков

Существуют разные способы зашифровать "облако". Один из них - поместить в облако крипто-диск. В предыдущей статье мы писали, почему это не всегда удобно.

Смотрим плавность хода с помощью BMW Rheingold

Всем знакома ситуация, когда двигатель немного "троит", но пропусков зажигания нет...

Дельта синхронизация без облака

Ранее мы показывали разные способы синхронизации криптодиска между ПК и Android-устройством.

Разработка приложений для Android. Семь полезных рецептов


© Денис Колисниченко

В этой статье мы рассмотрим семь полезных рецептов, которые пригодятся практически любому Android-программисту. Сразу скажу: рецепты рассчитаны на то, что читатель сей статьи уже знаком с разработкой приложений для Android. Азов здесь не будет. Все рецепты будут написаны на Java, а сами проекты будут создаваться в Eclipse.

Рецепт 1. Получение информации о телефоне

Класс TelephonyManager можно использовать для получения информации о телефоне, определения его состояния и набора номера абонента. В первом рецепте мы поговорим о получении информации о телефоне.

Первым делом создаем проект приложения по умолчанию (пусть наше приложение будет называться TM). В файл манифеста нужно добавить следующую строку:

 

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

 

Без этого нельзя будет прочитать состояние телефона и наше приложение, увы, работать не будет.

 

Рис. 1. Редактирование файла манифеста

 

Файл разметки тоже практически будет без изменения. Мы добавим лишь свойство id для элемента TextView. Файл разметки приведен в листинге 1.

 

Листинг 1. Файл разметки TM/res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    >

<TextView 

    android:id="@+id/info"

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:text="@string/hello"

    />

</LinearLayout>

 

После создания файла разметки можно приступить к написанию кода. Примерный код см. в листинге 2. Полный код приложения ты найдешь на компакт-диске (такое решение было принято, дабы не растягивать текст статьи) в файле TMActivity.java.

 

Листинг 2. Фрагмент кода приложения

 

String EOL = "\n";

// Находим текстовую область в разметке

info =(TextView) findViewById(R.id.info);

// Создаем объект tm для получения информации о телефоне

        tm = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);

        // буфер строк

        StringBuilder sb = new StringBuilder();

       

        // Общая информация об устройстве

        sb.append("Общая информация:\n\n");

        sb.append("ID устройства :")

        .append(tm.getDeviceId()).append(EOL);

        sb.append("Версия ПО: ")

        .append(tm.getDeviceSoftwareVersion()).append(EOL);

        sb.append("Номер телефона: ")

        .append(tm.getLine1Number()).append(EOL);

       

        // Информация об операторе, выдавшем SIM-карту

        sb.append("\nОператор:\n\n");

        sb.append("Код страны (ISO): ")

        .append(tm.getSimCountryIso()).append(EOL);

        sb.append("Оператор: ")

        .append(tm.getSimOperator()).append(EOL);

        sb.append("Название оператора: ")

        .append(tm.getSimOperatorName()).append(EOL);

        sb.append("Серийный номер SIM-карты: ")

        .append(tm.getSimSerialNumber()).append(EOL);

       

        // Информация о текущей сети

        sb.append("\nСеть:\n\n");

        sb.append("Код страны (ISO): ")

        .append(tm.getNetworkCountryIso()).append(EOL);

        sb.append("Оператор сети: ")

        .append(tm.getNetworkOperator()).append(EOL);

        sb.append("Название оператора сети: ")

        .append(tm.getNetworkOperatorName()).append(EOL);

       

        // Голосовая почта и другая информация

        sb.append("\nДругая информация:\n\n");

        sb.append("ID подписчика: ")

        .append(tm.getSubscriberId()).append(EOL);

        sb.append("Альфа-тег голосовой почты: ")

        .append(tm.getVoiceMailAlphaTag()).append(EOL);

        sb.append("Номер голосового почтового ящика: ")

        .append(tm.getVoiceMailNumber()).append(EOL);

 

        // Выводим содержимое буфера строк в текстовую область

        info.setText(sb.toString());

 

Думаю, код достаточно закомментирован и не нуждается в дополнительных комментариях. Мы создаем экземпляр класса TelephonyManager и "вытягиваем" из него полезную информацию.

 

Работающее приложение представлено на рис. 2.

 

Рис. 2. Информация о телефоне

 

Рецепт 2. Набор номера

 

Теперь разберемся, как набрать номер телефона. Чтобы получить разрешение на набор номера, добавляем в файл манифеста такую строку:

 

<uses-permission android:name="android.permission.CALL_PHONE" />

Затем ты можешь использовать одну из двух операций — или ACTION_CALL или ACTION_DIAL. Первая операция отобразит диалог с набираемым номером (как обычно при наборе номера вручную), вторая операция наберет номер без показа какого-либо интерфейса пользователя.

Пример использования этих двух операций:

startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:номер")));

startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse("tel:номер")));

 

Как видишь, ничего сложного и можно сразу же перейти к третьему рецепту, где мы определим номер входящего звонка.

 

Рецепт 3. Получение информации о состоянии телефона

 

Для прослушивания состояния телефона с целью ожидания какого-то события используются "прослушки", которые подробно описаны на странице руководства разработчика Android:

https://developer.android.com/reference/android/telephony/PhoneStateListener.html

Мы рассмотрим только наиболее часто используемую "прослушку" PhoneStateListener.LISTEN_CALL_STATE, позволяющую определить номер входящего звонка (и, соответственно, выполнить определенные действия при входящем звонке). Возможны три состояния звонка:

q      CALL_STATE_IDLE — устройство не используется для телефонного звонка (не принимается входящий звонок и не устанавливается исходящий);

q      CALL_STATE_RINGING — устройство принимает входящий звонок;

q      CALL_STATE_OFFHOOK — пользователь говорит по телефону.

 

Сейчас мы напишем программу, которая реагирует на все три состояния звонка и ничего не делает — действия ты определишь сам. Такое решение было принято, дабы не захламлять код. А что делать, решай сам — можешь, например, вывести уведомление при получении звонка. Чтобы задать собственные действия при изменении состояния звонка, мы переопределим метод onCallStateChanged().

Прежде, чем приступить к написанию кода, добавляем в файл манифеста следующую строку:

 

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

 

Файл разметки будет таким же, как у приложения TM (см. листинг 1). Фрагмент кода приложения представлен в листинге 3. Полный код приложения ты найдешь на CD в файле CallState.java.

 

Листинг 3. Реакция на изменение состояния звонка

...

import android.telephony.PhoneStateListener;

import android.telephony.TelephonyManager;

...

 

info =(TextView) findViewById(R.id.info);

// Создаем объект класса TelephonyManager

tm = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);

// Устанавливаем "прослушку" для LISTEN_CALL_STATE

tm.listen(new TelListener(),PhoneStateListener.LISTEN_CALL_STATE);

...

 

private class TelListener extends PhoneStateListener {

public void onCallStateChanged(int state, String incomingNumber) {

super.onCallStateChanged(state, incomingNumber);

switch (state) {

case TelephonyManager.CALL_STATE_IDLE:

  info.setText("IDLE");

                break;

case TelephonyManager.CALL_STATE_OFFHOOK:

  info.SetText("OFFHOOK, Вход. номер:" +incomingNumber);

                break;

case TelephonyManager.CALL_STATE_RINGING:

                info.SetText("RINGING, Вход. номер:" +incomingNumber);

                break;

              default:

         break;

  } // switch

     } // onCallStateChanged

   }

}

 

Наше приложение выводит в текстовую область (TextView) с именем info состояние телефона и номер входящего звонка, если таковой имеется.

 

Рецепт 4. Работаем с сенсорами мобилки

Современные мобилки оснащены всевозможными датчиками (сенсорами) - камерой, акселерометром, датчиком температуры и т.д.

Наиболее популярным сенсором является камера, но управление нею заслуживает отдельного разговора — мы обязательно поговорим об управлении камерой, но чуть позже. А пока рассмотрим другие датчики, которыми может быть оснащено мобильное устройство:

q      TYPE_ACCELEROMETER — акселерометр, позволяет определить ускорение мобильного устройство. Таким датчиком оснащаются не все смартфоны, преимущественно акселерометр можно найти на смартфонах, оснащенных функцией GPS (хотя это необязательно — все зависит от производителя устройства).

q      TYPE_LIGHT — датчик света. Очень полезная штука: с его помощью можешь управлять подсветкой дисплея, например, увеличить яркость, когда стало темно. В конечном итоге датчик помогает экономить заряд аккумулятора.

q      TYPE_TEMPERATURE — температурный датчик.

q      TYPE_PRESSURE — датчик атмосферного давления.

 

В твоем устройстве могут быть дополнительные датчики. Используй метод getSensorList() класса SensorManager, чтобы получить список датчиков.

Сейчас мы рассмотрим чтение показаний датчика температуры. Примеры чтения других датчиков можно найти в документации разработчика Android.

Для чтения датчиков подключаем следующие пакеты:

 

import android.hardware.Sensor;

import android.hardware.SensorEvent;

import android.hardware.SensorEventListener;

import android.hardware.SensorManager;

 

Далее определить объекты класса SensorManager:

private SensorManager myManager = null;

 

myManager = (SensorManager)getSystemService(SENSOR_SERVICE);

 

myManager.registerListener(tempSensorListener,

myManager.getDefaultSensor(Sensor.TYPE_TEMPERATURE),

SensorManager.SENSOR_DELAY_GAME);

 

Методу registerListener() передают три параметра. Первый — название обработчика датчика температуры. В нашем случае — это tempListener, который будет определен позже. Второй параметр — датчик по умолчанию, в нашем случае — датчик температуры. Третий параметр задает время обновления показаний датчика. Для наиболее быстрого обновления используйте SENSOR_DELAY_GAME, для обычного обновления — SENSOR_DELAY_NORMAL.

Следующий код определяет "прослушку" tempListener. Наша задача — переопределить методы onAccuracyChanged() и onSensorChanged(). Первый метод нам не нужен, поэтому мы определим его как пустой метод. А второй метод будет устанавливать текст области info (элемент TextView в разметке) — мы будем показывать температуру.

 

private final SensorEventListener tempListener = new SensorEventListener(){

 

@Override

public void onAccuracyChanged(Sensor sensor, int accuracy) {}

 

@Override

public void onSensorChanged(SensorEvent event) {

if(event.sensor.getType()==Sensor.TYPE_TEMPERATURE){

  info.setText("Температура: "+event.values[0]);

        }

       }

};

Дополнительную информацию можно получить по адресу:

https://developer.android.com/reference/android/hardware/SensorManager.html

Рецепт 5. Включаем виброзвонок

Для привлечения внимания к уведомлению или диалогу программы нужно использовать вибрацию. Для управления виброзвонком добавляем в файл манифеста следующее разрешение:

<uses-permission android:name="android.permission.VIBRATE" />

 

Далее используем класс Vibrator так:

Vibrator Vib = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);

Vib.vibrate(3000); // Вибрировать 3 секунды

 

Метод cancel() используется для преждевременной отмены вибрации (например, если ты установил вибрацию 3 секунды, а пользователь отреагировал раньше):

Vib.cancel();

Рецепт 6. Передача данных по Bluetooth

Для передачи данных с использованием Bluetooth нужно:

q      Включить адаптер Bluetooth.

q      Найти доступные Bluetooth-устройства.

q      Подключиться к одному из устройств.

q      Произвести, собственно, обмен данными.

 

В файл манифеста Android-приложения, использующего Bluetooth, добавляем строки:

<uses-permission android:name="android.permission.BLUETOOTH" />

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

 

В пакете android.bluetooth определены следующие классы:

q      BluetoothAdapter — представляет интерфейс обнаружения и установки Bluetooth-соединений.

q      BluetoothClass — описывает общие характеристики Bluetooth-устройства.

q      BluetoothDevice — представляет удаленное Bluetooth-устройство.

q      BluetoothSocket — сокет или точка соединения для данных, которыми наша система обменивается с другим Bluetooth-устройством.

q      BluetoothServerSocket — сокет для прослушивания входящих Bluetooth-соединений.

 

Включение Bluetooth-адаптера

 

Первым делом получаем адаптер по умолчанию:

BluetoothAdapter myBluetooth = BluetoothAdapter.getDefaultAdapter();

 

Активировать Bluetooth-адаптер можно с помощью следующего кода:

// Если Bluetooth-выключен

if(!myBluetooth.isEnabled()) {

// Создаем действие ACTION_REQUEST_ENABLE — запрашивает включение

// адаптера

Intent eIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

// Выполняем действие

startActivity(eIntent);

}

 

Обнаружение устройств по соседству

 

Для обнаружения соседних устройств используется код из листинга 4. Обнаруженные устройства попросту выводятся в журнал с помощью Log.d().

Листинг 4. Поиск Bluetooth-устройств

import android.util.Log;

...

private final BroadcastReceiver myReceiver = new BroadcastReceiver() {

   public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

// Когда найдено устройство

if (BluetoothDevice.ACTION_FOUND.equals(action)) {

// Получаем объект BluetoothDevice из Intent

BluetoothDevice device = intent.getParcelableExtra(

BluetoothDevice.EXTRA_DEVICE);

                // Выводим сообщение в журнал (его можно будет просмотреть

                // в Eclipse при запуске приложения).

Log.v("BlueTooth Discovery: ",device.getName() + "\n"

+ device.getAddress());

}

   }

};

IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);

registerReceiver(myReceiver, filter);

myBluetooth.startDiscovery();

Установка соединения с Bluetooth-устройством

 

Можно разработать как приложение-сервер, которое будет ожидать входящих запросов, так и приложение-клиент, которое будет устанавливать запрос с сервером. В листинге 5 приведен код, ожидающий соединения от программы-клиента.

 

Листинг 5. Ожидание запроса на подключение от клиента

// Класс AcceptBluetoothThread принимает входящие запросы

private class AcceptBluetoothThread extends Thread {

 

  private final BluetoothServerSocket myServerSocket;

 

  public AcceptThread() {

        // Используем временный объект, который позже

        // будет присвоен члену myServerSocket, поскольку

        // myServerSocket — финальный член класса и потом уже

        // не может быть изменен

BluetoothServerSocket tmp = null;

try {

// MY_UUID — идентификатор, также используемый клиентом

tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID);

} catch (IOException e) { }

// Присваиваем tmp члену класса myServerSocket

myServerSocket = tmp;

  }

  public void run() {

BluetoothSocket socket = null;

// Прослушиваем соединения

while (true) {

try {  // Принимаем соединение

socket = myServerSocket.accept();

} catch (IOException e) {

break;

}

// Если соединение было принято

if (socket != null) {

// Производим обработку соединения — в отдельном потоке

DoSomethingWith(socket);

// После обработки соединения закрываем сокет

myServerSocket.close();

break;

}

   }

}

/** Действие в случае отмены соединения */

  public void cancel() {

try {  // Закрываем сокет

myServerSocket.close();

} catch (IOException e) { }

  }

}

Теперь осталось написать приложение-клиент, устанавливающее соединение с Bluetooth-сокетом. Пример класса, который используется для установки соединения, приведен в листинге 6.

Листинг 6. Установка соединения с сервером (с Bluetooth-сокетом)

private class ConnectThread extends Thread {

  private final BluetoothSocket mySocket;

  private final BluetoothDevice myDevice;

 

  public ConnectThread(BluetoothDevice device) {

    // Используем временный объект, который позже

    // будет присвоен члену mySocket, поскольку

    // mySocket — финальный член класса и потом уже

    // не может быть изменен

    BluetoothSocket tmp = null;

    myDevice = device;

    // Получаем BluetoothSocket для соединения с BluetoothDevice

    try {

      // MY_UUID — идентификатор, такой же использует сервер

      tmp = device.createRfcommSocketToServiceRecord(MY_UUID);

    } catch (IOException e) { }

    mySocket = tmp;

    }

 

    public void run() {

    // Отключаем обнаружение устройств, поскольку оно замедляет

    // соединение

    mAdapter.cancelDiscovery();

    try {

// Соединяемся с устройством через сокет

mySocket.connect();

    } catch (IOException connectException) {

// Невозможно подключиться, закрываем сокет

try {

mySocket.close();

} catch (IOException closeException) { }

       return;

      }

  // Соединение установлено, производим его обработку в

  // отдельном потоке

  DoSomethingWith(mySocket);

}

// Отмена соединения, закрываем сокет

  public void cancel() {

try {

mySocket.close();

} catch (IOException e) { }

   }

}

Рецепт 7. Работа с камерой

Наконец-то мы поговорим о доступе к камере. Есть два способа доступа к камере. Первый заключается в вызове стандартного интерфейса управления камерой. Второй заключается в использовании класса Camera. Все зависит от поставленной задачи.

Начнем с первого способа. Для отображения стандартного интерфейса управления камерой используется следующий код:

Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");

startActivity(intent);

Просто? Да. Но не интересно. Если ты — настоящий программист, тебя интересует второй, более сложный способ.

Перед использованием класса Camera необходимо добавить в файл манифеста соответствующе разрешение:

<uses-permission android:name="android.permission.CAMERA" />

Для работы с камерой нам понадобятся следующие классы:

q      Camera — доступ к "железу" (непосредственно к самой камере);

q      Camera.Parameters — позволяет указать параметры камеры, например, размер изображения, качество изображения и т.д.

q      SurfaceView — позволяет выделить поверхность, которая будет использоваться в качестве области предварительного просмотра для камеры.

 

Создайте новый проект MyCamera (имя пакета com.samples.mycamera). Основной файл разметки res/layout/main.xml для нашего проекта приведен в листинге 4. Элемент SurfaceView будет использоваться как область предварительного просмотра камеры.

Листинг 4. Основной файл разметки main.xml

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical">

<SurfaceView android:id="@+id/surface"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

</SurfaceView>

</LinearLayout>

 

Управляющий камерой интерфейс будет описан в отдельном файле разметки. Назовите его cameraui.xml и поместите в каталог res/layout. Самое главное - это описание кнопки Получить фото, оно и приведено в листинге 5. А остальной код ты найдешь на CD в файле cameraui.xml.

 

Листинг 5. Файл cameraui.xml

...

<Button

android:id="@+id/button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Получить фото"

/>

...

 

Теперь приступаем к коду. Метод takePicture(), используемый для получения фото, требует указания трех методов:

q      SurfaceCallback() — используется для управления поверхностью, может также использоваться для применения различных графических эффектов к полученному фото;

q      PictureCallback() — используется для управления памятью — что делать с фото, если не хватило памяти;

q      CompressionCallback() — используется для сжатия фото.

 

Для управления поверхностью (surface) используется интерфейс SurfaceHolder.Callback. Нам нужно переопределить три его метода:

q      surfaceCreated() — вызывается во время создания поверхности, используется для инициализации объектов;

q      surfaceChanged — вызывается после создания поверхности и когда изменены параметры поверхности, например, ее размер;

q      surfaceDestroyed() — вызывается при удалении поверхности, используется для очистки памяти.

 

Полный листинг приложения с моими комментариями находится на CD в файле MyCamera.java. Фрагмент кода приведен в листинге 8.

 

Листинг 8. Приложения MyCamera (фрагмент кода)

public class MyCamera extends Activity implements SurfaceHolder.Callback {

   private LayoutInflater mInflater = null;

   Camera MyCam;                       // Камера

   byte[] tempdata;                    // Массив для временных данных

   boolean mPreviewRunning = false;

   // Поверхность и владелец поверхности (SurfaceHolder)

   private SurfaceHolder PreviewHolder;

   private SurfaceView Preview;

   // Кнопка

   Button GetPicture;

  

   @Override

   public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

getWindow().setFormat(PixelFormat.TRANSLUCENT);

requestWindowFeature(Window.FEATURE_NO_TITLE);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

WindowManager.LayoutParams.FLAG_FULLSCREEN);

setContentView(R.layout.main);

// Находим поверхность и устанавливаем SurfaceHolder

Preview = (SurfaceView)findViewById(R.id.surface);

PreviewHolder = Preview.getHolder();

PreviewHolder.addCallback(this);

PreviewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

mInflater = LayoutInflater.from(this);

View overView = mInflater.inflate(R.layout.cameraoverlay, null);

this.addContentView(overView,

new LayoutParams(LayoutParams.FILL_PARENT,

LayoutParams.FILL_PARENT));

        // Находим кнопку

GetPicture = (Button) findViewById(R.id.button);

// Устанавливаем обработчик нажатия кнопки

GetPicture.setOnClickListener(new OnClickListener(){

public void onClick(View view){

        // Вызываем метод takePicture() для получения картинки

MyCam.takePicture(Shutter, PicCallback, compress);

}

    });

}

 

// Пустая заглушканичего не делаем

ShutterCallback Shutter = new ShutterCallback(){

@Override

public void onShutter() {}

};

 

// Пустая заглушка — ничего не делаем

PictureCallback PicCallback = new PictureCallback() {

public void onPictureTaken(byte[] data, Camera c) {}

};

 

// Заполняем массив с временными данными и вызываем

// функцию done()

PictureCallback compress = new PictureCallback() {

public void onPictureTaken(byte[] data, Camera c) {

if(data !=null) {

tempdata=data;

done();

}

}

};

 

void done() {

        // Получаем растровое изображение путем декодирования

        // массива tempdata

Bitmap bm = BitmapFactory.decodeByteArray(tempdata,

0, tempdata.length);

String url = Images.Media.insertImage(getContentResolver(),

bm, null, null);

bm.recycle();

Bundle bundle = new Bundle();

if(url!=null) {

bundle.putString("url", url);

Intent mIntent = new Intent();

mIntent.putExtras(bundle);

setResult(RESULT_OK, mIntent);

} else {

Toast.makeText(this, "Ошибка получения картинки",

Toast.LENGTH_SHORT).show();

}

finish();

}

 

// Реакция на изменение поверхности

@Override

public void surfaceChanged(SurfaceHolder holder, int format,

int w, int h) {

try {

if (mPreviewRunning) {

MyCam.stopPreview();

mPreviewRunning = false;

}

// Получаем параметры камеры

Camera.Parameters p = MyCam.getParameters();

// Устанавливаем размер пред. просмотра

p.setPreviewSize(w, h);

// Устанавливаем параметры камеры

MyCam.setParameters(p);

// Устанавливаем владельца поверхности

MyCam.setPreviewDisplay(holder);

        // Запускаем пред. просмотр

MyCam.startPreview();

// Флаг пред. просмотра

mPreviewRunning = true;

} catch(Exception e) {

// Действие в случае исключения, для упрощения кода

// оставлено незаполненным. Можно вывести номер

// ошибки как уведомление с помощью метода

// e.toString()

}

}

 

@Override

public void surfaceCreated(SurfaceHolder holder) {

// Действие при создании поверхности — открываем камеру

MyCam = Camera.open();

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

        // Останавливаем пред. просмотр

MyCam.stopPreview();

// Сбрасываем флаг

mPreviewRunning = false;

// Освобождаем ресурсы

MyCam.release();

MyCam=null;

   }

}

На этом все. Буду рад выслушать ваши замечания и предложения по адресу dhsilabs@mail.ru.