Ага, а вот и продолжение истории о ноутбуках “для всех”. Первую часть можно почитать здесь.

Итак, покупка. Как это было? Было это мм.. довольно странно. Договаривался я с братьсями-китайцами две недели, письма писать они умеют, пишут вежливо: “Dear Sir, …” и все такое. В общем-то две недели общения посредством e-mail даром не прошли, информации я узнал об устройствах достаточно и это только укрепило мое желание сделать покупку. Сказали стоимость доставки: $224 если отправлять с помощью EMS (Express Mail Service). Почему именно EMS? Потому что они доставляют посылки каким-то чудесным образом и за них не надо платить таможенный сбор, который у нас может составоять до 30% от стоимости посылки. Именно из-за таможенных сборов мне пришлось отказаться от отправки с помощью DHL, хотя, конечно, я им как-то больше верю, сколько раз с ними работал, всегда все получалось, а FedEx не рассматривался по той причине что качество предоставляемых услуг у них ээ.. как бы это сказать, ниже плинтуса.

Да, в общем сошлись на EMS. Посчитаю-ка я общую стоимость, что у нас получается $162 * 5 + 224 итого $1034. Это получается почти по $207 за устройство. Фигасе!, – подумал я, а как же ж “для всех”? А как же ж “сверхдешевый да еще и на Linux’е”? Это ж 2/3 стоимости EEE PC! Но делать нечего, “Двадцать тыщ уже уплочено” (с) Масяня.

Прислали инвойс. Перевод сделал ПриватБанком. На удивление довольно шустро все произошло, за 40 минут мне открыли лицевой счет (по-моему 30 гривен это стоило, около $6) и $12 взяли за перевод международный. Я даже удивился, в ПриватБанке обычно все ооочень медленно, а тут вот так получилось шустро. Хоть что-то приятное. Итого $1034 + 6 + 12 получается $1052. Это по $210 за штучку. М-да.. почти в два раза стоимость увеличилась от заявленной.

Итак, денежка ушла в Китай. В тот же день отправил братьям-китайцам скан квитанции об оплате и получил от них “большое спасибо, технологические образцы будут готовы в течении 2х недель”.

Ну OK, две так две, можно и подождать, до нового года еще далеко 🙂

Прошло две недели, отписываю им, мол “как там дела с железками”, на что получаю, что-то типа “мы тут как бы пытаемся выпустить новую версию устройства со встроенным Wi-FI модулем, давайте мы вам новые пришлем образцы, но чуть позже”. Слово “Фигасе!” почему-то оять посетило мою голову, при чем тут встроенный вайфай? Я ж проплатил внешний. Ну, говорю, смотрите, я вам за внешний заплатил, дайте мне чего я хочу, просто вышлите и все. В ответ тишина…

Прошла неделя, опять им пишу, мол чего там с железом-то? Я тут заждался уже! На что получаю ответ “ну вот мы тут еще и экранчик улучшили, стало больше цветов и разрешение улучшилось и там еще помните, встроенный вайфай… но этот время, мы это все готовим… но если чего, можем вернуть вам деньги”. Ооо, отлично, экранчик.. но как же $60 за внешние вайфай модули? В общем, решил подождать, обещали разрешение 800х480 (оказывается раньше было меньше!).

Подождал до начал аоктября, опять тишина. Отписываю, мол, ну как там? Уже сделали? Опять тишина. Но я настойчивый, каждые три дня писал им письма 🙂 Наконец-то вот три дня назад получил ответ что мы отправили DHL’ом. Трекинг намбер бла-бла-бла.

Оооо! Отправили! Но DHL! Это получается что за все это добро мне прийдется платить таможенный сбор! Ну ёкарныбабай, мы же договаривались! С таким раскладом мне эти устройства выйдут по стоимости как EEE PC! Решил было успокоиться, но не ту-то было. На сайте DHL указанный tracking number оказался невалидным. Для пущей уверенности пошел проверить на сайте EMS, может в письме ошиблись с названием компании-курьера… Попробовал, то же самое, невалидный номер.

Сегодня отписал им по этому поводу, жду ответа, нервничаю…

Продолжение этой истории напишу как появятся новые сведения.

ЗЫ: У кого-нибудь еще есть опыт покупки оборудования в Китае? Как это было?

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

This post has 16 Comments

16
  1. Конечно же здаваться раньше веремни не стоит, но можете забыть про свои деньги.

    У меня знакомый заказывал у китайцев партию мотороллеров.
    На заводе выбрал модели, подписал контракт, внес оплату.
    Через 2 месяца приехал контейнер с каким-то барахлом, которое и мотороллером назвать нельзя.
    Связался с изготовителем, описал ситуацию – сказали “Да? Ну бывает! Присылайте назад – мы вам вышлем то что надо”. (стоимость перевозки $10-$12K)
    Ещё несколько месяцев пытался выбить с них возврат денег.
    Вроде вернули, но только за год всей этой тра%%%мудии он потерял около $22K, и это не считая своих поездок в Поднебесную ($1500 – перелет туда-назад)

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

    Вообще с китайскими компаниями дел лучше не иметь – они со всем соглашаются, кивают головами, улыбаются, а делают по своему. При этом они полностью безнаказанны, и сами это знают.
    Кто не согласен – можете попробовать подать на них в суд 🙂

    P.S. Да и вы тоже молодец ещё тот – заказывать в Китае(!!) ноуты по $130(!!!) партией 5(!!!) штук 🙂

  2. Твоя ошибка была на том моменте когда ты заказал товар не контролируя процесс находясь в китае. Никогда не верь китайцам! Они переплюнут негров и евреев вместе взятых. Все кто с ними работает стоят у них над душой иначе никак. Был у знакомых случай с мотороллерами. Заказали модели количества и цвета. Пришел контейнер а там грубо говоря металлолом. Спросить не с кого. Кивают головой и делают вид что не понимают. Если с ними и работать то только если находишься там сам или есть надежный экспидитор который будет сопровождать товар от а до я.

  3. 2Vadim Voituk
    Ну, мне интересно было, мм.. просто у каждого есть определенная сумма, которую он готов потратить на хобби. Т.е. да, я знаю, риски большие, но, думаю, раз попробовать можно. Если бы раньше знал больше об услугах экспедиторов, думаю, воспользовался бы этим. Сейчас даже оказалось что среди знакомых есть люди, которые могли бы помочь с этим.
    С другой же стороны, я устройства заказывал, прежде всего, для себя и, допустим, 100 штук как они предлагают партии, я бы скорее всего не заказал, а с 5 ноутбуками скорее всего никакой экспедитор не будет возиться, по крайней мере по словам тех кто с ними работает.
    В принципе риск осознанный.

  4. ну алибаба и прочие wholesale сайты – это сплошной разводняк.
    у меня московские знакомые китайскую микроэлектронику возят. но там по договоренности с заводом. поездки в китай и т.п.
    то что тебя уже кинули даже нет сомнений. пиши в банк – пытайся вернуть деньги

    2 Артем
    >”Они переплюнут негров и евреев вместе взятых.”

    <тут дальше было много всякого. смысл такой что гражданин Артем очень неправ...> (edited by T-Rex)

  5. Уважаемый. Да вам лечится надо. Где тут нацизм мой дорогой одноклеточный друг. Я бы с радостью с тобой встретился. Если ты в Киеве дам даже адрес и тел. и искать не надо. 😉

  6. Они переплюнут негров и евреев вместе взятых
    2nikolai
    На самом деле в словах Артема есть немалая доля правды. Многие еще помнят “нигерийские письма”, из-за которых, например на eBay почти половина продавцов пишут “will not ship to Nigeria”.
    По поводу евреев ничего сказать не могу, не торговал с ними, зато было несколько заказчиков из Израиля… Ну, работа с ними, я бы сказал, не самая приятная.

    И постарайтесь как-то более цивилизованно выражать свои мысли.

  7. Радует только то, что хоть что-то пришло в коробках. Хотя брак – это аут.

  8. Главное чтобы теперь не вышли из строя те нетбуки что работают. А то будет совсем грустно.

  9. Из 5ти девайсов:
    – 3 загружаются только будучи включенніми в розетку
    – 1 работает только будучи включеннім в розетку
    – 1 перезагружается постоянно при старте

    ПО, входящее в комплект вроде все рабочее. Музыку играет нормально, видео притормаживает. ИМХО, лучше б туда вместо линуха перепрошить Windows CE или Android OS. первый вариант более стабильным мне кажется, второй более эстетичным.

    Решать проблему с загрузкой девайсов еще не пробовал, некогда. Думаю там проблема или в зарядном устройстве или в батарейках.

Leave a Reply

Your email address will not be published. Required fields are marked *

Р.

Работа с акселерометром в Android

Для одного из текущих проектов понадобилась поддержка акселерометра. Учитывая то, что еще месяц назад Android API я в глаза не видел, мне казалось что получение данных с акселерометра – это какой-то адский труд. Оказалось все намного проще.

Для работы с различными датчиками в Android используется класс Sensor. Список датчиков можно получить через SensorManager. Например таким вот образом при создании Activity можно получить объект Sensor, связанный с акселеромтером:

public class AccelerometerTest extends Activity {

	SensorManager mSensorManager;
	Sensor mAccelerometerSensor;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
        if(sensors.size() > 0)
        {
        	for (Sensor sensor : sensors) {
        		switch(sensor.getType())
        		{
        		case Sensor.TYPE_ACCELEROMETER:
        			if(mAccelerometerSensor == null) mAccelerometerSensor = sensor;
        			break;
        		default:
        			break;
        		}
		}
        }
    }


Для того, чтобы получать данные с акселерометра нам необходимо проделать еще несколько несложных операций:

  • Реализовать интерфейс SensorEventListener
  • , в частности нас интересует метод onSensorChanged()

  • Реализовать метод onResume() где подписать Activity на сообщения от акселеромтера
  • Реализовать метод onPause() где отписать Activity от сообщений акселерометра
public class AccelerometerTest extends Activity implements SensorEventListener {
    @Override
    protected void onPause() {
    	mSensorManager.unregisterListener(this);
    	super.onPause();

    }

    @Override
    protected void onResume() {
    	super.onResume();
    	mSensorManager.registerListener(this, mAccelerometerSensor, SensorManager.SENSOR_DELAY_GAME);
    	mSensorManager.registerListener(this, mMagneticFieldSensor, SensorManager.SENSOR_DELAY_GAME);
    }

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

	@Override
	public void onSensorChanged(SensorEvent event) {
		float [] values = event.values;
		switch(event.sensor.getType())
		{
		case Sensor.TYPE_ACCELEROMETER:
			{
				// Здесь можно обрабатывать данные от сенсора
			}
			break;
		}
	}
}

Простейший пример отображения данных – отображать их в TextView

public class AccelerometerTest extends Activity implements SensorEventListener {
	SensorManager mSensorManager;
	Sensor mAccelerometerSensor;

	TextView mForceValueText;
	TextView mXValueText;
	TextView mYValueText;
	TextView mZValueText;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		...
		mForceValueText = (TextView)findViewById(R.id.value_force);
		mXValueText = (TextView)findViewById(R.id.value_x);
		mYValueText = (TextView)findViewById(R.id.value_y);
		mZValueText = (TextView)findViewById(R.id.value_z);
	}
	...
	@Override
	public void onSensorChanged(SensorEvent event) {
		float [] values = event.values;
		switch(event.sensor.getType())
		{
		case Sensor.TYPE_ACCELEROMETER:
			{
				mXValueText.setText(String.format("%1.3f", 
					event.values[SensorManager.DATA_X]));
				mYValueText.setText(String.format("%1.3f", 
					event.values[SensorManager.DATA_Y]));
				mZValueText.setText(String.format("%1.3f", 
					event.values[SensorManager.DATA_Z]));

				double totalForce = 0.0f;
				totalForce += Math.pow(
					values[SensorManager.DATA_X]/SensorManager.GRAVITY_EARTH, 2.0);
				totalForce += Math.pow(
					values[SensorManager.DATA_Y]/SensorManager.GRAVITY_EARTH, 2.0);
				totalForce += Math.pow(
					values[SensorManager.DATA_Z]/SensorManager.GRAVITY_EARTH, 2.0);
				totalForce = Math.sqrt(totalForce);
				mForceValueText.setText(String.format("%1.3f", totalForce));
			}
			break;
		}
	}
}

res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<TableLayout 	android:id="@+id/TableLayout01"
				android:layout_width="fill_parent"
				android:layout_height="fill_parent"
				xmlns:android="http://schemas.android.com/apk/res/android">
	<TableRow 	android:layout_width="wrap_content"
				android:layout_height="wrap_content"
				android:id="@+id/row_force"
				android:layout_margin="5dip">
		<TextView 	android:layout_width="wrap_content"
					android:layout_height="wrap_content"
					android:id="@+id/label_force"
					android:text="Force:"
					android:gravity="right"></TextView>
		<TextView 	android:layout_height="wrap_content"
					android:layout_width="fill_parent"
					android:layout_weight="1"
					android:id="@+id/value_force"
					android:text="-"
					android:layout_marginLeft="5dip"></TextView>
	</TableRow>
	<TableRow 	android:layout_height="wrap_content"
				android:layout_width="fill_parent"
				android:id="@+id/row_x"
				android:layout_margin="5dip">
		<TextView 	android:layout_width="wrap_content"
					android:layout_height="wrap_content"
					android:text="X:"
					android:id="@+id/label_x"
					android:gravity="right"></TextView>
		<TextView	android:layout_height="wrap_content"
					android:text="-"
					android:layout_width="fill_parent"
					android:layout_weight="1"
					android:id="@+id/value_x"
					android:layout_marginLeft="5dip"></TextView>
	</TableRow>
	<TableRow 	android:layout_height="wrap_content"
				android:layout_width="fill_parent"
				android:id="@+id/row_y"
				android:layout_margin="5dip">
		<TextView 	android:layout_width="wrap_content"
					android:layout_height="wrap_content"
					android:text="Y:"
					android:id="@+id/label_y"
					android:gravity="right"></TextView>
		<TextView 	android:layout_height="wrap_content"
					android:text="-"
					android:layout_width="fill_parent"
					android:layout_weight="1"
					android:id="@+id/value_y"
					android:layout_marginLeft="5dip"></TextView>
	</TableRow>
	<TableRow 	android:layout_height="wrap_content"
				android:layout_width="fill_parent"
				android:id="@+id/row_z"
				android:layout_margin="5dip">
		<TextView	android:layout_width="wrap_content"
					android:layout_height="wrap_content"
					android:text="Z:"
					android:id="@+id/label_z"
					android:gravity="right"></TextView>
		<TextView 	android:layout_height="wrap_content"
					android:text="-"
					android:layout_width="fill_parent"
					android:layout_weight="1"
					android:id="@+id/value_z"
					android:layout_marginLeft="5dip"></TextView>
	</TableRow>
</TableLayout>

В результате получим что-то подобное:

Из примера можно увидеть что в классе SensorManager есть константы DATA_X, DATA_Y, DATA_Z, которые используются в качетсве индексов в массиве значений, возвращаемых акселерометром.
Отображение данных в TextView – это, конечно, неплохо, но не дает общей картины изменений показаний акселерометра при изменении положения телефона. Для того, чтобы увидеть изменение показаний во времени, решил добавить отображение в виде графика.
Для создания графиков набрел на чудесную библиотеку AChartEngine. Библиотека бесплатная, доступна на Google Code.
Добавляем в layout пару кнопок – для начала/останова записи показаний акселерометра и для открытия окна с графиком.

<?xml version="1.0" encoding="utf-8"?>
<TableLayout 	android:id="@+id/TableLayout01"
				android:layout_width="fill_parent"
				android:layout_height="fill_parent"
				xmlns:android="http://schemas.android.com/apk/res/android">
...
<TableRow 	android:id="@+id/TableRow01"
				android:layout_height="wrap_content"
				android:layout_width="fill_parent">
		<ViewStub	android:id="@+id/ViewStub01"
					android:layout_width="wrap_content"
					android:layout_height="wrap_content"></ViewStub>
		<LinearLayout 	android:id="@+id/LinearLayout01"
						android:layout_height="wrap_content"
						android:layout_width="fill_parent"
						android:layout_weight="1">
			<Button 	android:layout_height="wrap_content"
						android:layout_weight="1"
						android:text="Start recording"
						android:layout_width="fill_parent"
						android:id="@+id/button_start"></Button>
			<Button 	android:layout_height="wrap_content"
						android:layout_weight="1"
						android:text="Show"
						android:layout_width="fill_parent"
						android:id="@+id/button_show"></Button>
		</LinearLayout>
	</TableRow>
</TableLayout>

В результате этих изменений получаем такой layout:

Теперь научим Activity реагировать на нажания кнопок:

package com.itdimension.accelerometertest;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import org.achartengine.*;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;

public class AccelerometerTest extends Activity implements SensorEventListener {
	...
	double margins[] = {0, 0};

	Button mStartButton;
	Button mShowButton;

	List<List<Double>> mValues;
	boolean mIsRecording = false;

	OnClickListener mStartButtonListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			mIsRecording = !mIsRecording;
			if(mIsRecording) {
				mValues.get(SensorManager.DATA_X).clear();
				mValues.get(SensorManager.DATA_Y).clear();
				mValues.get(SensorManager.DATA_Z).clear();
				margins[0] = 0;
				margins[1] = 0;
			}
		}
	};

	OnClickListener mShowButtonListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			try
			{
				Intent intent = getChartIntent();
				startActivity(intent);
			}
			catch (Exception e) {
				new AlertDialog.Builder(AccelerometerTest.this)
					.setTitle("Error")
					.setMessage(e.getMessage())
					.create()
					.show();
			}

		}
	};

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ...
        mValues = new ArrayList<List<Double>>();
        mValues.add(new ArrayList<Double>());
        mValues.add(new ArrayList<Double>());
        mValues.add(new ArrayList<Double>());
	...
        mStartButton = (Button)findViewById(R.id.button_start);
        mShowButton = (Button)findViewById(R.id.button_show);

        mStartButton.setOnClickListener(mStartButtonListener);
        mShowButton.setOnClickListener(mShowButtonListener);
    }
    ...

	@Override
	public void onSensorChanged(SensorEvent event) {
		float [] values = event.values;
		switch(event.sensor.getType())
		{
		case Sensor.TYPE_ACCELEROMETER:
			{
				if(mIsRecording)
				{
					recordSensorValue(event);
				}
				...
			}
			break;
		}
	}

	private void recordSensorValue(SensorEvent event) {
		double value;
		for(int i = SensorManager.DATA_X; i <= SensorManager.DATA_Z; i++)
		{
			value = (double)event.values[i];
			margins[0] = Math.min(margins[0], value);
			margins[1] = Math.max(margins[1], value);
			mValues.get(i).add(value);
		}
	}

	Intent getChartIntent() {
		int [] colors = new int[] { 
		      Color.RED, Color.GREEN, Color.BLUE };
		PointStyle[] styles = new PointStyle[] { 
		      PointStyle.POINT, PointStyle.POINT, PointStyle.POINT };
		XYMultipleSeriesRenderer renderer = buildRenderer(colors, styles);
		setChartSettings(renderer, "Sensor Values", "Index", "Value",
	    		0,
	    		mValues.get(SensorManager.DATA_X).size(),
	    		margins[0] * 1.5,
	    		margins[1] * 1.5,
	        	Color.GRAY, Color.LTGRAY);
		return ChartFactory.getLineChartIntent(this, buildDataset(), renderer);
	}

	protected void setChartSettings(XYMultipleSeriesRenderer renderer, 
		      String title, String xTitle,
		      String yTitle, double xMin, 
		      double xMax, double yMin, double yMax, 
		      int axesColor, int labelsColor) {
		    renderer.setChartTitle(title);
		    renderer.setXTitle(xTitle);
		    renderer.setYTitle(yTitle);
		    renderer.setXAxisMin(xMin);
		    renderer.setXAxisMax(xMax);
		    renderer.setYAxisMin(yMin);
		    renderer.setYAxisMax(yMax);
		    renderer.setAxesColor(axesColor);
		    renderer.setLabelsColor(labelsColor);
		  }

	protected XYMultipleSeriesRenderer buildRenderer(int[] colors, PointStyle[] styles) {
	    XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
	    int length = colors.length;
	    for (int i = 0; i < length; i++) {
	      XYSeriesRenderer r = new XYSeriesRenderer();
	      r.setColor(colors[i]);
	      r.setPointStyle(styles[i]);
	      renderer.addSeriesRenderer(r);
	    }
	    return renderer;
	  }

	XYMultipleSeriesDataset buildDataset() {
		XYMultipleSeriesDataset result = new XYMultipleSeriesDataset();
		XYSeries xSeries = new XYSeries("X");
		XYSeries ySeries = new XYSeries("Y");
		XYSeries zSeries = new XYSeries("Z");

		int count = mValues.get(SensorManager.DATA_X).size();
		for(int i = 0; i < count; i++)
		{
			xSeries.add(i, mValues.get(SensorManager.DATA_X).get(i));
			ySeries.add(i, mValues.get(SensorManager.DATA_Y).get(i));
			zSeries.add(i, mValues.get(SensorManager.DATA_Z).get(i));
		}

		result.addSeries(xSeries);
		result.addSeries(ySeries);
		result.addSeries(zSeries);

		return result;
	}
}

После всех этих манипуляций, при нажатии на кнопку “Show” получим приблизительно такой график:


Ну вот, на этом пока все.
Скачать исходный код примера можно здесь.

В.

В сети появились первые скриншоты Firefox Mobile (Fennec)

В сети появилась информация о том что Mozilla CEO Джон Лилли (John Lilly) в интервью Mercury News заявил о том что мобильная версия браузера Firefox (Fennec) должна появиться в течении месяца. Также он представил первые скриншоты Fennec.

Веб-разработчикам, наверное, будет интересен тем факт что Fennec получил 88 баллов из ста в тесте Acid3.

На данный момент в списке официально заявленных мобильных платформ, для которых будет доступен Fennec, указана только UME (Ubuntu Mobile and Embedded)  для Nokia 8×0, но планируются также версии и для остальных популярных мобильных платформ.

Официальную информацию по этому поводу можно узнать здесь.

PS: Меня как разработчика ПО для мобильных устройств очень интересует, на сколько удобным будет процесс встраивания браузера в приложения на С++. Если в дистрибутиве Fennec, как и в дистрибутиве Firefox для десктонов, разработчики предусмотрят наличие примеров, то вполне возможно что этот браузер станет довольно удобным инструментом разработки ПО для Ubuntu Mobile and Embedded ибо на данный момент более-менее комфорстно можно пользоваться только WebKit’ом, и то, для этого требуются длительные танцы с бубном %)