Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 89487 сообщений
Cообщения с меткой

андроид - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

Android Architecture Components. Часть 4. ViewModel

Суббота, 05 Августа 2017 г. 10:28 (ссылка)

image



Компонент ViewModel — предназначен для хранения и управления данными, связанными с представлением, а заодно, избавить нас от проблемы, связанной с пересозданием активити во время таких операций, как переворот экрана и т.д. Не стоит его воспринимать, как замену onSaveInstanceState, поскольку, после того как система уничтожит нашу активити, к примеру, когда мы перейдем в другое приложение, ViewModel будет также уничтожен и не сохранит свое состояние. В целом же, ViewModel можно охарактеризовать как синглтон, который гарантирует, что не будет уничтожен пока есть активный экземпляр нашей активити и освободит ресурсы после ухода с нее (все немного сложнее, но выглядит как-то так). Стоит также отметить, что мы можем привязать любое количество ViewModel к нашей Activity(Fragment).



Компонент состоит из таких классов: ViewModel, AndroidViewModel, ViewModelProvider, ViewModelProviders, ViewModelStore, ViewModelStores. Разработчик будет работать только с  ViewModel, AndroidViewModel и для получения истанца с ViewModelProviders, но для лучшего понимания компонента, мы поверхностно рассмотрим все классы.





Класс ViewModel, сам по себе представляет абстрактный класс, без абстрактных методов и с одним protected методом onCleared(). Для реализации собственного ViewModel, нам всего лишь необходимо унаследовать свой класс от ViewModel с конструктором без параметров и это все. Если же нам нужно очистить ресурсы, то необходимо переопределить метод onCleared(), который будет вызван когда ViewModel долго не доступна и должна быть уничтожена. Как пример, можно вспомнить предыдущую статью про LiveData, а конкретно о методе observeForever(Observer), который требует явной отписки, и как раз в методе onCleared() уместно ее реализовать. Стоит еще добавить, что во избежания утечки памяти, не нужно ссылаться напрямую на View или Context Activity из ViewModel. В целом, ViewModel должна быть абсолютно изолированная от представления данных. В таком случае появляется вопрос: А каким же образом нам уведомить представление (Activity/Fragment) об изменениях в наших данных? В этом случае на помощь нам приходит LiveData, все изменяемые данные мы должны хранить с помощью LiveData, если же нам необходимо, к примеру, показать и скрыть ProgressBar, мы можем создать MutableLiveData и хранить логику показать\скрыть в компоненте ViewModel. В общем это будет выглядеть так:

public class MyViewModel extends ViewModel {
  private MutableLiveData showProgress = new MutableLiveData<>();

  //new thread
  public void doSomeThing(){
      showProgress.postValue(true);
      ...
      showProgress.postValue(false);
  }
 
  public MutableLiveData getProgressState(){
      return showProgress;
  }
}




Для получения ссылки на наш экземпляр ViewModel мы должны воспользоваться ViewModelProviders:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  final MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
  viewModel.getProgressState().observe(this, new Observer() {
      @Override
      public void onChanged(@Nullable Boolean aBoolean) {
          if (aBoolean) {
              showProgress();
          } else {
              hideProgress();
          }
      }
  });
  viewModel.doSomeThing();
}




Класс AndroidViewModel, являет собой расширение ViewModel, с единственным отличием — в конструкторе должен быть один параметр Application. Является довольно полезным расширением в случаях, когда нам нужно использовать Location Service или другой компонент, требующий Application Context. В работе с ним единственное отличие, это то что мы наследуем наш ViewModel от ApplicationViewModel. В Activity/Fragment инициализируем его точно также, как и обычный ViewModel.



Класс ViewModelProviders, являет собой четыре метода утилиты, которые, называются of и возвращают ViewModelProvider. Адаптированные для работы с Activity и Fragment, а также, с возможностью подставить свою реализацию ViewModelProvider.Factory, по умолчанию используется DefaultFactory, которая является вложенным классом в ViewModelProviders. Пока что других реализаций приведенных в пакете android.arch нет.



Класс ViewModelProvider, собственно говоря класс, который возвращает наш инстанс ViewModel. Не будем особо углубляться здесь, в общих чертах он являет роль посредника с ViewModelStore, который, хранит и поднимает наш интанс ViewModel и возвращает его с помощью метода get, который имеет две сигнатуры get(Class) и get(String key, Class modelClass). Смысл заключается в том, что мы можем привязать несколько ViewModel к нашему Activity/Fragment даже одного типа. Метод get возвращает их по String key, который по умолчанию формируется как: «android.arch.lifecycle.ViewModelProvider.DefaultKey:» + canonicalName



Класс ViewModelStores, являет собой фабричный метод, напомню: Фабричный метод — паттерн, который определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать, по факту, позволяет классу делегировать инстанцирование подклассам. На данный момент, в пакете android.arch присутствует как один интерфейс, так и один подкласс ViewModelStore.



Класс ViewModelStore, класс в котором и находится вся магия, состоит из методов put, get и clear. Про них не стоит беспокоится, поскольку работать напрямую мы с ними не должны, а с get и put и физически не можем, так как они объявлены как default (package-private), соответственно видны только внутри пакета. Но, для общего образования, рассмотрим устройство этого класса. Сам класс хранит в себе HashMap, методы get и put, соответственно, возвращают по ключу (по тому самому, который мы формируем во ViewModelProvider) или добавляют ViewModel. Метод clear(), вызовет метод onCleared() у всех наших ViewModel которые мы добавляли.



Для примера работы с ViewModel давайте реализуем небольшое приложение, позволяющее выбрать пользователю точку на карте, установить радиус и показывающее, находится человек в этом поле или нет. А также дающее возможность указать WiFi network, если пользователь подключен к нему, будем считать что он в радиусе, вне зависимости от физических координат.







Для начала создадим две LiveData для отслеживания локации и имени WiFi сети:

public class LocationLiveData extends LiveData implements
      GoogleApiClient.ConnectionCallbacks,
      GoogleApiClient.OnConnectionFailedListener,
      LocationListener {
  private final static int UPDATE_INTERVAL = 1000;
  private GoogleApiClient googleApiClient;

  public LocationLiveData(Context context) {
      googleApiClient =
              new GoogleApiClient.Builder(context, this, this)
                      .addApi(LocationServices.API)
                      .build();
  }

  @Override
  protected void onActive() {
      googleApiClient.connect();
  }

  @Override
  protected void onInactive() {
      if (googleApiClient.isConnected()) {
          LocationServices.FusedLocationApi.removeLocationUpdates(
                  googleApiClient, this);
      }
      googleApiClient.disconnect();
  }

  @Override
  public void onConnected(Bundle connectionHint) {
          LocationRequest locationRequest = new LocationRequest().setInterval(UPDATE_INTERVAL).setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
          LocationServices.FusedLocationApi.requestLocationUpdates(
                  googleApiClient, locationRequest, this);
  }

  @Override
  public void onLocationChanged(Location location) {
      setValue(location);
  }

  @Override
  public void onConnectionSuspended(int cause) {
      setValue(null);
  }

  @Override
  public void onConnectionFailed(ConnectionResult connectionResult) {
      setValue(null);
  }
}



public class NetworkLiveData extends LiveData {
  private Context context;
  private BroadcastReceiver broadcastReceiver;

  public NetworkLiveData(Context context) {
      this.context = context;
  }

  private void prepareReceiver(Context context) {
      IntentFilter filter = new IntentFilter();
      filter.addAction("android.net.wifi.supplicant.CONNECTION_CHANGE");
      filter.addAction("android.net.wifi.STATE_CHANGE");
      broadcastReceiver = new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
              WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
              WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
              String name = wifiInfo.getSSID();
              if (name.isEmpty()) {
                  setValue(null);
              } else {
                  setValue(name);
              }
          }
      };
      context.registerReceiver(broadcastReceiver, filter);
  }

  @Override
  protected void onActive() {
      super.onActive();
      prepareReceiver(context);
  }

  @Override
  protected void onInactive() {
      super.onInactive();
      context.unregisterReceiver(broadcastReceiver);
      broadcastReceiver = null;
  }
}




Теперь перейдем к ViewModel, поскольку у нас есть условие, которое зависит от полученных данных с двух LifeData, нам идеально подойдет MediatorLiveData как холдер самого значения, но поскольку перезапускать сервисы нам невыгодно, поэтому подпишемся к MediatorLiveData без привязки к жизненному циклу с помощью observeForever.  В методе onCleared() реализуем отписку от него с помощью removeObserver. В свою же очередь LiveData будет уведомлять об изменении MutableLiveData, на которую и будет подписано наше представление.    



public class DetectorViewModel extends AndroidViewModel {
//для хранения вводимых данных, решил создать Repository, листинг его можно посмотреть на GitHub по линке в конце материала
  private IRepository repository;
  private LatLng point;
  private int radius;
  private LocationLiveData locationLiveData;
  private NetworkLiveData networkLiveData;
  private MediatorLiveData statusMediatorLiveData = new MediatorLiveData<>();
  private MutableLiveData statusLiveData = new MutableLiveData<>();
  private String networkName;
  private float[] distance = new float[1];
  private Observer locationObserver = new Observer() {
      @Override
      public void onChanged(@Nullable Location location) {
          checkZone();
      }
  };
  private Observer networkObserver = new Observer() {
      @Override
      public void onChanged(@Nullable String s) {
          checkZone();
      }
  };
  private Observer mediatorStatusObserver = new Observer() {
      @Override
      public void onChanged(@Nullable Status status) {
          statusLiveData.setValue(status.toString());
      }
  };

  public DetectorViewModel(final Application application) {
      super(application);
      repository = Repository.getInstance(application.getApplicationContext());
      initVariables();
      locationLiveData = new LocationLiveData(application.getApplicationContext());
      networkLiveData = new NetworkLiveData(application.getApplicationContext());
      statusMediatorLiveData.addSource(locationLiveData, locationObserver);
      statusMediatorLiveData.addSource(networkLiveData, networkObserver);
      statusMediatorLiveData.observeForever(mediatorStatusObserver);
  }

//Для того чтобы зря не держать LocationService в работе, мы от него отписываемся если WiFi network подходит.
  private void updateLocationService() {
      if (isRequestedWiFi()) {
          statusMediatorLiveData.removeSource(locationLiveData);
      } else if (!isRequestedWiFi() && !locationLiveData.hasActiveObservers()) {
          statusMediatorLiveData.addSource(locationLiveData, locationObserver);
      }
  }

//считываем данные с репозитория
  private void initVariables() {
      point = repository.getPoint();
      if (point.latitude == 0 && point.longitude == 0)
          point = null;
      radius = repository.getRadius();
      networkName = repository.getNetworkName();
  }

//метод, который отвечает за проверку того находимся мы в нужной зоне или нет
  private void checkZone() {
      updateLocationService();
      if (isRequestedWiFi() || isInRadius()) {
          statusMediatorLiveData.setValue(Status.INSIDE);
      } else {
          statusMediatorLiveData.setValue(Status.OUTSIDE);
      }
  }

  public LiveData getStatus() {
      return statusLiveData;
  }
// методы которые отвечают за запись данных в репозиторий
  public void savePoint(LatLng latLng) {
      repository.savePoint(latLng);
      point = latLng;
      checkZone();
  }

  public void saveRadius(int radius) {
      this.radius = radius;
      repository.saveRadius(radius);
      checkZone();
  }

  public void saveNetworkName(String networkName) {
      this.networkName = networkName;
      repository.saveNetworkName(networkName);
      checkZone();
  }

  public int getRadius() {
      return radius;
  }

  public LatLng getPoint() {
      return point;
  }

  public String getNetworkName() {
      return networkName;
  }

  public boolean isInRadius() {
      if (locationLiveData.getValue() != null && point != null) {
          Location.distanceBetween(locationLiveData.getValue().getLatitude(), locationLiveData.getValue().getLongitude(), point.latitude, point.longitude, distance);
          if (distance[0] <= radius)
              return true;
      }
      return false;
  }

  public boolean isRequestedWiFi() {
      if (networkLiveData.getValue() == null)
          return false;
      if (networkName.isEmpty())
          return false;
      String network = networkName.replace("\"", "").toLowerCase();
      String currentNetwork = networkLiveData.getValue().replace("\"", "").toLowerCase();
      return network.equals(currentNetwork);
  }

  @Override
  protected void onCleared() {
      super.onCleared();
      statusMediatorLiveData.removeSource(locationLiveData);
      statusMediatorLiveData.removeSource(networkLiveData);
      statusMediatorLiveData.removeObserver(mediatorStatusObserver);
  }
}




И наше представление:



public class MainActivity extends LifecycleActivity {
  private static final int PERMISSION_LOCATION_REQUEST = 0001;
  private static final int PLACE_PICKER_REQUEST = 1;
  private static final int GPS_ENABLE_REQUEST = 2;
  @BindView(R.id.status)
  TextView statusView;
  @BindView(R.id.radius)
  EditText radiusEditText;
  @BindView(R.id.point)
  EditText pointEditText;
  @BindView(R.id.network_name)
  EditText networkEditText;
  @BindView(R.id.warning_container)
  ViewGroup warningContainer;
  @BindView(R.id.main_content)
  ViewGroup contentContainer;
  @BindView(R.id.permission)
  Button permissionButton;
  @BindView(R.id.gps)
  Button gpsButton;
  private DetectorViewModel viewModel;
  private LatLng latLng;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      ButterKnife.bind(this);
      checkPermission();
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
      if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
          init();
      } else {
          showWarningPage(Warning.PERMISSION);
      }
  }

  private void checkPermission() {
      if (PackageManager.PERMISSION_GRANTED == checkSelfPermission(
              Manifest.permission.ACCESS_FINE_LOCATION)) {
          init();
      } else {
          requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_LOCATION_REQUEST);
      }
  }


  private void init() {
      viewModel = ViewModelProviders.of(this).get(DetectorViewModel.class);
      if (Utils.isGpsEnabled(this)) {
          hideWarningPage();
          checkingPosition();
          initInput();
      } else {
          showWarningPage(Warning.GPS_DISABLED);
      }
  }

  private void initInput() {
      radiusEditText.setText(String.valueOf(viewModel.getRadius()));
      latLng = viewModel.getPoint();
      if (latLng == null) {
          pointEditText.setText(getString(R.string.chose_point));
      } else {
          pointEditText.setText(latLng.toString());
      }
      networkEditText.setText(viewModel.getNetworkName());
  }

  @OnClick(R.id.get_point)
  void getPointClick(View view) {
      PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
      try {
          startActivityForResult(builder.build(MainActivity.this), PLACE_PICKER_REQUEST);
      } catch (GooglePlayServicesRepairableException e) {
          e.printStackTrace();
      } catch (GooglePlayServicesNotAvailableException e) {
          e.printStackTrace();
      }
  }

  @OnClick(R.id.save)
  void saveOnClick(View view) {
      if (!TextUtils.isEmpty(radiusEditText.getText())) {
          viewModel.saveRadius(Integer.parseInt(radiusEditText.getText().toString()));
      }
      viewModel.saveNetworkName(networkEditText.getText().toString());
  }

  @OnClick(R.id.permission)
  void permissionOnClick(View view) {
      checkPermission();
  }

  @OnClick(R.id.gps)
  void gpsOnClick(View view) {
      startActivityForResult(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS), GPS_ENABLE_REQUEST);
  }


  private void checkingPosition() {
      viewModel.getStatus().observe(this, new Observer() {
          @Override
          public void onChanged(@Nullable String status) {
              updateUI(status);
          }
      });
  }

  private void updateUI(String status) {
      statusView.setText(status);
  }

  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (requestCode == PLACE_PICKER_REQUEST) {
          if (resultCode == RESULT_OK) {
              Place place = PlacePicker.getPlace(data, this);
              updatePlace(place.getLatLng());
          }
      }
      if (requestCode == GPS_ENABLE_REQUEST) {
          init();
      }
  }

  private void updatePlace(LatLng latLng) {
      viewModel.savePoint(latLng);
      pointEditText.setText(latLng.toString());
  }

  private void showWarningPage(Warning warning) {
      warningContainer.setVisibility(View.VISIBLE);
      contentContainer.setVisibility(View.INVISIBLE);
      switch (warning) {
          case PERMISSION:
              gpsButton.setVisibility(View.INVISIBLE);
              permissionButton.setVisibility(View.VISIBLE);
              break;
          case GPS_DISABLED:
              gpsButton.setVisibility(View.VISIBLE);
              permissionButton.setVisibility(View.INVISIBLE);
              break;
      }
  }

  private void hideWarningPage() {
      warningContainer.setVisibility(View.GONE);
      contentContainer.setVisibility(View.VISIBLE);
  }
}




В общих чертах мы подписываемся на MutableLiveData, с помощью меnода getStatus() из нашего ViewModel. А также работаем с ним для инициализации и сохранения наших данных.

Здесь также добавлено несколько проверок, таких как RuntimePermission и проверка на состояние GPS. Как можно заметить, код в Activity получился довольно обширный, в случае сложного UI, гугл рекомендует посмотреть в сторону создания презентера(но это может быть излишество).



В примере также использовались такие библиотеки как:

compile 'com.jakewharton:butterknife:8.6.0'
compile 'com.google.android.gms:play-services-maps:11.0.2'
compile 'com.google.android.gms:play-services-location:11.0.2'
compile 'com.google.android.gms:play-services-places:11.0.2'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0'




Полный листинг: here



Полезные ссылки: here и here



Android Architecture Components. Часть 1. Введение

Android Architecture Components. Часть 2. Lifecycle

Android Architecture Components. Часть 3. LiveData

Android Architecture Components. Часть 4. ViewModel
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/334942/

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Геометрия в компьютерных играх

Четверг, 03 Августа 2017 г. 21:37 (ссылка)

Всем привет! Когда-то давным-давно я делал простенькие игрушки на Flash. Например: игрушка — провести курсор мышки через лабиринт, не касаясь стен и уворачиваясь от всяких движущихся объектов. Некоторые из этих объектов двигаются по заданной траектории, некоторые гонятся за курсором, а некоторые стреляют в курсор другими движущимися объектами.



Сейчас я увлёкся программированием под андроид и сделал примерно такую же игрушку. И столкнулся с теми же геометрическими задачками с которыми встречался тогда.







Задачка 1: нарисовать стены. И сразу возникает Задачка 2: определить касается ли точка стены или нет (проиграл ты или продолжать игру).



Для этого я поделил стены на фигуры: прямоугольники и многоугольники.



С прямоугольниками всё просто: Просто нарисовать:



canvas.drawRect(x1, y1, x2, y2, paint);


и просто проверить находится ли точка внутри него или нет.



public boolean isTouched(float x, float y){
return (x>x1)&&(xy1)&&(ycode>


С многоугольником дело обстоит не так просто: нарисовать его уже немного сложнее.



Path path = new Path();
paint.setStyle(Paint.Style.FILL);
path.moveTo(x1, y1);
path.lineTo(x2, y2);
path.lineTo(x3, y3);
.....
path.lineTo(x1, y1); //замыкаем фигуру
path.close();
canvas.drawPath(path, paint);


А проверить касание с точкой ещё сложнее. Я попытался вспомнить школьный курс геометрии, потом погуглил и нашёл такое решение:



Многоугольник обязательно должен быть выпуклым.







И описывать его надо обязательно по часовой стрелке.







При этом каждое ребро имеет начальные и конечные координаты, то есть по сути является вектором. И можно определить находится ли точка справа или слева от него.



Для этого есть простая формула.



private boolean isLeftHandSituated(float dotX, float dotY, float x1, float y1, float x2, float y2){
float d = (dotX - x1) * (y2 - y1) - (dotY - y1) * (x2 - x1);
return d>0;
}


Чтобы определить внутри ли точка или снаружи я в цикле перебираю все рёбра, и если хоть для одного ребра точка находится слева (в данном случае это ребро BC) — значит она снаружи, так как многогранник описан по часовой стрелке.



Задачка 3: жёлтый смайлик. Если точка приближается к нему на определённое расстояние, то он гонится за точкой. Вычислить это определённое расстояние (distance) можно по теореме Пифагора:



float dx = dotX - smileX;
float dy = dotY - smileY;
double distance = Math.sqrt((dx * dx + dy * dy));


Смайлик гонится за точкой. Что это значит? Это значит что через каждый определённый интервал времени (в следующем кадре) его координаты перемещаются на определённое расстояние ближе к точке. Это расстояние за кадр по сути является скоростью. Я назвал переменную speed.







Вычислить координаты смайлика в следующем кадре можно так:



private float speed=5;
double rate = speed / distance;
newSmileX = smileX + (float) (dx * rate);
newSmileY = smileY + (float) (dy * rate);


Задачка 4: пушка всё время направлена на точку. Как вычислить угол на который её надо повернуть? Очень просто. Для этого существует метод atan2.



float dx = dotX - cannonX;
float dy = dotY - cannonY;
double theta = Math.atan2(dx, dy); //получаем угол в радианах
angle = Math.toDegrees(theta); //переводим его в градусы.


Заключение: Статья получилась довольно сжатая и короткая, но, надеюсь, полезная многим начинающим разработчикам игр. Всем удачи в обучении и разработке!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/334848/

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Математический пакет для Android — «Микро-Математика» — теперь с открытым исходным кодом

Вторник, 02 Августа 2017 г. 00:16 (ссылка)

Некоторое время назад я писал здесь о «Микро-Математике» — математическом пакете для Android, который я разработал в качестве хобби-поделки. Этим летом исполняется три года с тех пор, как «Микро-Математика» была выложена в Google Play на всеобщее обозрение. С тех пор программа развивалась дальше, и вот настал момент, когда доход от Google Play окупил разработку. В связи с этим я не вижу смысла дальше утаивать исходный код от общественности и перевожу проект в разряд Open Source. Тех, кому интересно познакомиться с репозиторием «Микро-Математики» на github, и, быть может, поучаствовать в дальнейшем развитии проекта, прошу под кат.



Я не буду повторять здесь содержание предыдущей статьи — идеология, ядро и базовый функционал приложения с тех пор не изменились. Естественно, за три года добавилось много новых возможностей. Выделю наиболее важные, на мой взгляд:




  • Реализован функциональный файловый менеджер, который поддерживает не только SD-карту, но и доступ к ресурсам приложения. За основу взято ядро достаточно известного файлового менеджера «Ghost Commander»

    Окно файлового менеджера




  • В ресурсах приложения теперь содержится большая библиотека примеров.

    Если у тех, кто пользуется этим приложением, есть интересные примеры вычислений, и вы не против поделиться ими с общественностью, высылайте их мне по почте, я с удовольствием включу их в эту библиотеку.

    Окно выбора примера




  • Теперь приложение может работать с комплексными числами:

    Пример расчёта с комплексными чмслами




  • Кроме этого, добавлена поддержка массивов, использование которых позволяет значительно ускорить расчёт

    Пример расчёта с использованием массивов




  • Появился экспорт документа в формате HTML, после чего его можно открыть в браузере, например, на ПК:

    Экспортированный документ на ПК




  • Добавлена возможность автоматического тестирования математического ядра. Модуль-тестировщик встроен в приложение, он позволяет прогонять скрипты, имеющие специальный синтаксис и контролировать результаты вычислений. На сегодня разработаны 30 скриптов, которые содержат более 250 тестов, покрывающих все вычислительные алгоритмы.



Github репозиторий находится тут. Лицензия — GNU General Public License v3.0.



Так уж исторически сложилось, что вся разработка у меня происходит под Linux, и, стыдно сказать, до сих пор в Eclipse. Но я открыт к альтернативным рабочим окружениям, так что если кто-то захочет запуллить конвертацию служебных скриптов под Windows, или проектные файлы для Android Studio — буду только рад.



На текущий момент в репозитории содержится как весь исходный код и SVG-исходники всех иконок приложения, так и некоторые вспомогательные вещи:




  • src: папка с исходниками. Там можно найти: сами исходники, шаблон форматирования для Eclipse, а также в папке src/assets документацию, библиотеку примеров и скрипты автоматического тестирования

  • images: SVG-исходники иконок и шелл-скрипты для их конвертации в PNG-ресурсы. Для работы скриптов необходим установленный Inkscape.

  • thirdParty: архивы всех использованных библиотек.

  • release: собранный, но не подписанный APK. Там же находится архив с результатами автоматического тестирования

  • autotest: служебные скрипты для генерации тестовых виртуальных машин и для прогона на них тестов.



За последнее время я получил много пожеланий по дальнейшему развитию приложения. Среди них могу выделить такие, как:




  • Экспорт в PDF

  • Матричные вычисления

  • Циклы, как, например, это реализовано в Matcad

  • Улучшение редактирования: перетаскивание формул пальцем, более удобный доступ к палитре мат. символов.

  • Внедрение пакетной системы для группировки мат. функционала: например, опциональные пакеты по обработке сигналов, статистике, специальные функции.

  • Я уже использую библиотеку «The Apache Commons Mathematics Library», где есть модуль символьных вычислений. Неплохо было бы его задействовать.



Для одного человека работы многовато, поэтому приглашаю к сотрудничеству всех энтузиастов, кто интересуется математикой, алгоритмами, численными методами и т.д. Спасибо за внимание!






Как часто Вы присоединяетесь к чужим Open Source проектам?
















































Проголосовало 14 человек. Воздержалось 7 человек.





Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/334670/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Developer Preview 4 уже доступна. Официальный запуск платформы Android O совсем скоро

Среда, 26 Июля 2017 г. 13:28 (ссылка)

Дейв Бёрк, вице-президент Google по инженерии



Пока мы дорабатываем платформу Android O, вы можете познакомиться с Developer Preview 4, которая поможет вам заблаговременно обновить свои приложения.



Developer Preview 4 — последняя предварительная версия перед официальным запуском платформы Android O, который состоится уже этим летом. Сейчас у вас есть возможность протестировать и опубликовать обновления (или обновленные) приложений. Так переход на Android O пройдет комфортно для пользователей.



Если у вас есть устройство, которое участвует в Программе бета-тестирования Android, вы получите обновление Developer Preview 4 в ближайшие дни. Если ваше устройство не включено в программу, зарегистрируйте его на сайте, и мы пришлем вам обновление.



Следите за информацией об официальном запуске Android O!



Что нового?



Developer Preview 4 — это предварительная версия Android O, которая позволяет обновить и протестировать ваши приложения к официальному запуску новой платформы. Developer Preview 4 уже очень близка к финальной версии: в нее включены все последние исправления и оптимизации. Тестовая система содержит в себе API 26 версии, которые были доступны еще в Developer Preview 3.



Мы представляем образы системы Developer Preview 4 для устройств, а вместе с ней и стабильную версию Android 26.0.0 Support Library. Соответствующие обновления для SDK, инструментов и образы системы Android Emulator вы увидите в ближайшие несколько дней.



Мы также хотим поделиться с вами обновленной версией Android Testing Support Library, которая включает в себя новые инструменты Android Test Orchestrator, Multiprocess Espresso и другие. Следите за новостями!



Тестируйте приложения на Android O



Образы системы Developer Preview 4 — это отличная возможность протестировать ваши приложения на версии Android O близкой к финальной. Тестирование позволит вам убедиться в том, что ваше приложение будет правильно работать после обновления до новой версии платформы.



Зарегистрируйте поддерживаемое устройство в Программе бета-тестирования Android и получите доступ к Developer Preview 4. Затем скачайте свое приложение из Google Play и протестируйте его — именно так оно будет выглядеть для обычного пользователя. Убедитесь, что интерфейс выглядит корректно, приложение быстро запускается и правильно обрабатывает изменения в поведении системы. В частности, обратите внимание на ограничения передачи данных о местоположении в фоновом режиме, оповещения, а также на изменения, которые коснулись сетевых подключений, безопасности и идентификаторов.



При необходимости исправьте все неполадки. Теперь вы можете опубликовать обновление для приложения, которое будет доступно после того, как пользователи начнут получать приглашения обновиться до Android O.

Улучшайте приложения с помощью новых функций Android O и API



Пользователи, которые используют последние версии Android, как правило, чаще других скачивают приложения, потребляют больше контента и совершают покупки. И они активнее сообщают о том, что любимые приложения должны поддерживать самые новые функции последних версий Android. В системе Android O пользователи ожидают увидеть в приложениях такие функции, как каналы нотификаций, точки-нотификации, закрепляемые ярлыки, картинка в картинке, автозаполнение и т. д. Эти же функции помогут вашему приложению в будущем привлечь новых пользователей, которые будут переходить на платформу Android O.



В Android O ярлыки можно вынести на главный экран прямо из приложения. Эта функция поможет повысить интерес пользователей к приложению. (слева)



Точки-нотификации поддерживают активность пользователей в приложении и позволяют им переходить сразу к ключевым функциям. (справа)



Улучшение вашего приложения с помощью новых функций Android O поможет вам привлечь пользователей, предложить им новые виды взаимодействия, обеспечить для них еще более высокий уровень контроля и безопасности, а также повысить производительность приложения. Такие функции, как адаптивные значки, скачиваемые шрифты и автоматическое изменение размера текста в TextView позволят упростить разработку приложения и уменьшить размер APK-файла. Время работы от батареи также играет важную роль для пользователей, поэтому они оценят оптимизацию вашего приложения с учетом ограничений фоновой работы и других важных изменений в поведении системы при запуске приложений на платформе Android O.



Посетите сайт версии Android O для разработчиков, чтобы узнать обо всех новых функциях и API, а также о том, как внедрить их в ваши приложения.



Разрабатывайте быстрее с Android Studio



Когда вы будете готовы к работе с Android O, скачайте последнюю версию Android Studio 3.0, доступную здесь. Помимо улучшенных инструментов проверки производительности приложения, поддержки языка программирования Kotlin и оптимизации инструмента Gradle, Android Studio 3.0 предлагает упрощенный процесс разработки благодаря мгновенным приложениям, XML-шрифтам, скачиваемым шрифтам и адаптивным значкам.



Мы рекомендуем обновиться до стабильной версии Android Support Library 26.0.0, которая доступна в хранилище Google's Maven, а также скачать последние версии SDK, инструменты и образы системы эмулятора, которые станут доступны в ближайшие дни.



Вы можете обновить compileSdkVersion своего проекта до API 26, чтобы компиляция проходила с использованием API официальной версии Android O. Также мы рекомендуем обновить targetSdkVersion вашего приложения до API 26, чтобы получить возможность протестировать приложение с учетом изменений поведения в системе Android O. Обратитесь к руководству по миграции, чтобы узнать подробнее о том, как настроить среду разработки для программирования под Android O.



Публикация обновлений в Google Play



Google Play открыт для приложений скомпилированных или перекомпилированных под API 26. Когда вы будете готовы, загрузите обновленную альфа-, бета- или рабочую версию своего приложения.



Убедитесь, что обновленное приложение запускается как на Android O, так и на более ранних версиях Android. Советуем вам воспользоваться функцией бета-тестирования на Google Play, чтобы получить первые отзывы о вашем приложении от небольшой группы пользователей. После этого начинайте поэтапный запуск. С нетерпением ждём ваших обновленных приложений!



Как получить Developer Preview 4



Если у вас ещё нет версии для разработчиков Developer Preview 4, получить ее очень просто! Перейдите на сайт android.com/beta и зарегистрируйте подходящий телефон или планшет. Кроме того, вы можете скачать обновление и установить его вручную. Версия Android O доступна для разработчиков на устройствах Pixel, Pixel XL, Pixel C, Nexus 5X, Nexus 6P, Nexus Player и на Android Emulator. Все зарегистрированные устройства будут обновлены автоматически после выпуска официальной версии Android O.



Спасибо вам за вклад, который вы сделали на предварительной стадии. Мы ждём от вас новых отзывов и предложений!

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/334180/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
casinozru

Покер на операционной системе Андроид

Воскресенье, 23 Июля 2017 г. 15:07 (ссылка)


73bb3349c7757935044324832503ae00 (700x466, 25Kb)



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



 



Играем в покер онлайн во смартфона



Разработанный интерфейс учитывает особенности устройств для игры, поэтому сенсорная панель управления полностью заменяет мышку. Удобный геймплей мобильных гаджетов положительно влияет на игру.



Читать далее...
Метки:   Комментарии (3)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Android Architecture Components. Часть 3. LiveData

Пятница, 21 Июля 2017 г. 19:15 (ссылка)

image

Компонент LiveData — предназначен для хранения объекта и разрешает подписаться на его изменения. Ключевой особенностью является то, что компонент осведомлен о жизненном цикле и позволяет не беспокоится о том на каком этапе сейчас находиться подписчик, в случае уничтожения подписчика, компонент отпишет его от себя. Для того чтоб LiveData учитывала жизненный цикл используется компонент Lifecycle, но также есть возможность использовать без привязки к жизненному циклу.



Сам компонент состоит из классов: LiveData, MutableLiveData, MediatorLiveData, LiveDataReactiveStreams, Transformations и интерфейса: Observer.



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



Для обновления значения мы должны передать его с помощью метода setValue(T), будьте внимательны поскольку этот метод нужно вызывать с main треда в противном случае мы получим IllegalStateException, если же нам нужно передать значение из другого потока можно использовать postValue(T), этот метод в свою очередь обновит значение в main треде. Интересной особенностью postValue(T) является еще то, что он в случае множественного вызова, не будет создавать очередь вызовов на main тред, а при исполнении кода в main треде возьмет последнее полученное им значение. Также в классе присутствует два калбека:

onActive() — будет вызван когда количество подписчиков изменит свое значение с 0 на 1.

onInactive() — будет вызван когда количество подписчиков изменит свое значение с 1 на 0.

Их назначение соответственно уведомить наш класс про то, что нужно обновлять даные или нет. По умолчанию они не имеют реализации, и для обработки этих событий мы должны переопределить эти методы.

Давайте рассмотрим как будет выглядеть наш LiveData класс, который будет хранить wife network name и в случае изменения будет его обновлять, для упрощения он реализован как синглтон.

public class NetworkLiveData extends LiveData {
  private Context context;
  private BroadcastReceiver broadcastReceiver;
  private static NetworkLiveData instance;

  public static NetworkLiveData getInstance(Context context){
      if (instance==null){
          instance = new NetworkLiveData(context.getApplicationContext());
      }
      return instance;
  }

  private NetworkLiveData(Context context) {
      this.context = context;
  }

  private void prepareReceiver(Context context) {
      IntentFilter filter = new IntentFilter();
      filter.addAction("android.net.wifi.supplicant.CONNECTION_CHANGE");
      filter.addAction("android.net.wifi.STATE_CHANGE");
      broadcastReceiver = new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
              WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
              WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
              String name = wifiInfo.getSSID();
              if (name.isEmpty()) {
                  setValue(null);
              } else {
                  setValue(name);
              }
          }
      };
      context.registerReceiver(broadcastReceiver, filter);
  }

  @Override
  protected void onActive() {
      prepareReceiver(context);
  }

  @Override
  protected void onInactive() {
      context.unregisterReceiver(broadcastReceiver);
      broadcastReceiver = null;
  }
}




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



Для того чтоб добавить подписчика есть два метода: observe(LifecycleOwner, Observer) — для добавления подписчика с учетом жизненного цикла и observeForever(Observer) — без учета. Уведомления об изменении данных приходят с помощью реализации интерфейса Observer, который имеет один метод onChanged(T).

Выглядит это приблизительно так:

public class MainActivity extends LifecycleActivity implements Observer {
  private TextView networkName;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      networkName = (TextView) findViewById(R.id.network_name);
      NetworkLiveData.getInstance(this).observe(this,this);
      //NetworkLiveData.getInstance(this).observeForever(this);
  }

  @Override
  public void onChanged(@Nullable String s) {
      networkName.setText(s);
  }
}




Примечание: Этот фрагмент только для примера,  не используйте этот код в реальном проекте. Для работы с LiveData лучше использовать ViewModel(про этот компонент в следующей статье) или позаботиться про отписку обсервера вручную.

В случае использования observe(this,this) при повороте экрана мы будем каждый раз отписываться от нашего компонента и заново подписываться. А в случае использование observeForever(this) мы получим memory leak.




Помимо вышеупомянутых методов в api LiveData также входит getValue(), hasActiveObservers(), hasObservers(), removeObserver(Observer observer), removeObservers(LifecycleOwner owner) в дополнительных комментариях не нуждаются.



Класс MutableLiveData, является расширением LiveData, с отличием в том что это не абстрактный класс и методы setValue(T) и postValue(T) выведены в api, тоесть публичные.

По факту класс является хелпером для тех случаев когда мы не хотим помещать логику обновления значения в LiveData, а лишь хотим использовать его как Holder.

void update(String someText){
      ourMutableLiveData.setValue(String);
}




Класс MediatorLiveData, как понятно из названия это реализация паттерна медиатор, на всякий случай напомню: поведенческий паттерн, определяет объект, инкапсулирующий способ взаимодействия множества объектов,  избавляя их от необходимости явно ссылаться друг на друга. Сам же класс расширяет MutableLiveData и добавляет к его API два метода: addSource(LiveData, Observer)  и removeSource(LiveData). Принцип работы с классом заключается в том что мы не подписываемся на конкретный источник, а на наш MediatorLiveData, а источники добавляем с помощью addSource(..). MediatorLiveData в свою очередь сам управляет подпиской на источники.  

Для примера создадим еще один класс LiveData, который будет хранить название нашей мобильной сети:

public class MobileNetworkLiveData extends LiveData {
  private static MobileNetworkLiveData instance;
  private Context context;

  private MobileNetworkLiveData(Context context) {
      this.context = context;
  }

  private MobileNetworkLiveData() {

  }

  @Override
  protected void onActive() {
      TelephonyManager telephonyManager = (TelephonyManager) context
              .getSystemService(Context.TELEPHONY_SERVICE);
      String networkOperator = telephonyManager.getNetworkOperatorName();
      setValue(networkOperator);
  }

  public static MobileNetworkLiveData getInstance(Context context) {
      if (instance == null) {
          instance = new MobileNetworkLiveData(context);
      }
      return instance;
  }

}




И перепишем наше приложение так чтоб оно отображало название wifi сети, а если подключения к wifi нет, тогда название мобильной сети, для этого изменим MainActivity:

public class MainActivity extends LifecycleActivity implements Observer {
  private MediatorLiveData mediatorLiveData;
  private TextView networkName;


  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      networkName = (TextView) findViewById(R.id.network_name);
      mediatorLiveData = new MediatorLiveData<>();
      init();
  }


  private void init() {
      final LiveData network = NetworkLiveData.getInstance(this);
      final LiveData mobileNetwork = MobileNetworkLiveData.getInstance(this);
      Observer networkObserver = new Observer() {
          @Override
          public void onChanged(@Nullable String s) {
              if (!TextUtils.isEmpty(s))
                  mediatorLiveData.setValue(s);
              else
                  mediatorLiveData.setValue(mobileNetwork.getValue());
          }
      };
      Observer mobileNetworkObserver = new Observer() {
          @Override
          public void onChanged(@Nullable String s) {
                  if (TextUtils.isEmpty(network.getValue())){
                      mediatorLiveData.setValue(s);
                  }
          }
      };
      mediatorLiveData.addSource(network, networkObserver);
      mediatorLiveData.addSource(mobileNetwork,mobileNetworkObserver);
      mediatorLiveData.observe(this, this);
  }


  @Override
  public void onChanged(@Nullable String s) {
      networkName.setText(s);
  }
}




Как мы можем заметить, теперь наш UI подписан на MediatorLiveData и абстрагирован от конкретного источника данных. Стоит обратить внимание на то что значения в нашем медиаторе не зависят напрямую от источников и устанавливать его нужно в ручную.



Класс LiveDataReactiveStreams, название ввело меня в заблуждение поначалу, подумал что это расширение LiveData с помощью RX, по факту же, класс являет собой адаптер с двумя static методами: fromPublisher(Publisher publisher), который возвращает объект LiveData и toPublisher(LifecycleOwner lifecycle, LiveData liveData), который возвращает объект Publisher. Для использования этого класса, его нужно импортировать отдельно:

compile «android.arch.lifecycle:reactivestreams:$version»



Класс Transformations, являет собой хелпер для смены типизации LiveData, имеет два static метода:

map(LiveData, Function)  - применяет в main треде реализацию интерфейса Function и возвращает объект LiveData

, где T — это типизация входящей LiveData, а P желаемая типизация исходящей, по факту же каждый раз когда будет происходить изменения в входящей LiveData она будет нотифицировать нашу исходящую, а та в свою очередь будет нотифицировать подписчиков после того как переконвертирует тип с помощью нашей реализации Function. Весь этот механизм работает за счет того что по факту исходящая LiveData, является MediatiorLiveData.


LiveData location = ...;

LiveData locationString = Transformations.map(location, new Function() {
  @Override
  public String apply(Location input) {
      return input.toString;
  }
});




switchMap(LiveData, Function>)  - похож к методу map с отличием в том, что вместо смены типа в функции мы возвращаем сформированный объект LiveData.

LiveData location = ...;
LiveData getPlace(Location location) = ...;

LiveData userName = Transformations.switchMap(location, new Function>() {
  @Override
  public LiveData apply(Location input) {
      return getPlace(input);
  }
});




Базовый пример можно посмотреть в репозитории: git    



Также полезные ссылки:

https://developer.android.com/topic/libraries/architecture/livedata.html

https://developer.android.com/reference/android/arch/lifecycle/LiveData.html
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333890/

Комментарии (0)КомментироватьВ цитатник или сообщество
юлия_ВОЛЧЕК

Пазлы с девушками

Четверг, 20 Июля 2017 г. 14:43 (ссылка)


https://play.google.com/store/apps/details?id=com.sbitsoft.pcbgirls



6037813_1 (700x410, 301Kb)



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



6037813_2 (700x410, 286Kb)



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



6037813_3 (700x410, 184Kb)



Пазлы с девушками – это 20 картинок с изображением прекрасных девушек. Каждая картинка состоит из 56 кусочков, которые нужно собрать воедино, и в результате увидеть красивое фото с девушкой. Собирая пазлы, можно выбрать режим с подсказкой, если поиск правильной позиции какого-либо кусочка вызывает затруднения.



6037813_7 (700x410, 241Kb)



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



6037813_8 (700x410, 178Kb)



Скачать игру можно по ссылке: https://play.google.com/store/apps/details?id=com.sbitsoft.pcbgirls

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Применение преобразования Фурье для создания гитарного тюнера на Android. Часть 1

Понедельник, 17 Июля 2017 г. 14:58 (ссылка)



В основе спектрального анализа звуковых данных лежит алгоритм, который носит название преобразование Фурье. При раскладывании исходного звукового сигнала на частотные составляющие, отдельные частоты называются гармониками. Основная гармоника определяет высоту звучания, а второстепенные гармоники определяют его тембр. Есть достаточно много мобильных приложений, которые используют преобразование Фурье для того, чтобы отобразить весь спектр частот (гармоник). Так же, есть мобильные приложения, которые служат для настройки гитар. Они работают по принципу: основная гармоника находится по самому высокому значению амплитуды в спектре. Такое утверждение не совсем верно, потому что основная гармоника определяется самой наименьшей из всех кратных этой гармонике, либо шагом между гармониками. Возникает необходимость найти способ, который позволит отобразить значение основной гармоники в спектре звукового сигнала.



В первой части статьи мы рассмотрим принцип работы дискретного преобразование Фурье, а также возможность записывать звуковые данные с Android устройства с помощью класса AudioRecord.



ДПФ, БПФ. Библиотека JTransforms.



Звуковые данные, записанные в виде импульсно-кодовой модуляции (PCM), показывают амплитуду звуковой волны в конкретный момент времени. В 19-м веке Жан Батист Фурье доказал, что любой периодический сигнал можно представить, как сумму бесконечного ряда простых синусоидальных сигналов. Из чего следует, что наш исходный звуковой сигнал, можно представить в виде суммы, упорядоченных по частоте, простых синусоидальных сигналов с собственными амплитудами. Тем самым перейти от одной зависимости (амплитуда-время) к другой (амплитуда-частота). Такое преобразование называется преобразованием Фурье.



Для вычисления преобразования Фурье на компьютерах ввели понятие дискретное преобразование Фурье (ДПФ), которое требует в качестве входа дискретную функцию.



Рассмотрим принцип работы дискретного преобразования Фурье в среде программирования Java. Для начала, опишем некоторую функцию, которая будет представлять сумму двух гармоник (косинусов) с частотами 100 Гц и 880 Гц соответственно.



private double someFun(int index, int sampleRate) {
final int amplitudeOfFirstHarmonic = 15;
final int amplitudeOfSecondHarmonic = 1;
final int frequencyOfFirstHarmonic = 100;
final int frequencyOfSecondHarmonic = 880;

return amplitudeOfFirstHarmonic * Math.cos((frequencyOfFirstHarmonic * 2 * Math.PI * index ) / sampleRate)
+ amplitudeOfSecondHarmonic *Math.cos((frequencyOfSecondHarmonic * 2 * Math.PI * index) / sampleRate);
}


В качестве частоты дискретизации возьмём значение 8000 Гц и заполним массив из 8000 элементов данными, вызывая функцию someFunc() в цикле



final int sampleRate = 8000;
final int someFuncSize = 8000;
double[] someFunc = new double[someFuncSize];
for (int i = 0; i < someFunc.length; i++) {
someFunc[i] = someFun(i, sampleRate);
}


В результате чего, получим массив значений, который определяет дискретное представление нашей функции. Для визуального представления полученных данных, выведем, полученные значения, из массива в файл .csv и построим график в программе Excel





Как видно из диаграммы, гармоника с частотой 100 Гц имеет амплитуду 15, значение которой и было указано в константе amplitudeOfFirstHarmonic. Поверх первой гармоники с частотой 100 Гц и амплитудой 15, рисуется вторая гармоника с частотой 880 Гц и амплитудой равной единице.

По такому же принципу создадим две базисные функции косинуса и синуса. Только теперь, мы будем передавать значение частоты в параметры методов наших базисных функций



private double cos(int index, int frequency, int sampleRate) {
return Math.cos((2 * Math.PI * frequency * index) / sampleRate);
}
private double sin(int index, int frequency, int sampleRate) {
return Math.sin((2 * Math.PI * frequency * index) / sampleRate);
}


Теперь, определим метод, который будет выполнять дискретное преобразование Фурье. В качестве параметров метода, передадим массив значений исходной дискретной функции, состоящей из суммы простых косинусов, и частоту дискретизации этой функции.



private double[] dft(double[] frame, int sampleRate) {
final int resultSize = sampleRate / 2;
double[] result = new double[resultSize * 2];
for (int i = 0; i < result.length / 2; i++) {
int frequency = i;
for (int j = 0; j < frame.length; j++) {
result[2*i] +=frame[j] * cos(j, frequency, sampleRate);
result[2*i + 1] +=frame[j] * sin(j, frequency, sampleRate);
}
result[2*i] =result[2*i] / resultSize;
result[2*i + 1] = result[2*i + 1] / resultSize;
}

return result;
}


После выполнения преобразования Фурье, полученные значения, определяют проекции всех векторов, упорядоченных по частоте, на оси косинусов и синусов. Для того, чтобы найти длину такого вектора, необходимо применить теорему Пифагора $inline$A=\sqrt{a^2+b^2}$inline$.



double[] result;
long start = System.currentTimeMillis();
result = dft(someFunc, sampleRate);
long finish = System.currentTimeMillis();
long timeConsumedMillis = finish - start;
System.out.println("Time's dft: " + timeConsumedMillis);
double[] amplitude = new double[sampleRate/2];
for (int i = 0; i < result.length / 2; i++) {
amplitude[i] = Math.sqrt(result[2*i]*result[2*i] + result[2*i+1]*result[2*i+1]);
System.out.println(i + ": " + "Projection on cos: " + result[2*i] + " Projection on sin: " + result[2*i + 1]
+ " amplitude: "+ amplitude[i] + "\n");
}


В результате выполнения кода из предыдущего листинга, мы получили представление нашей дискретной функции someFunc() в виде набора значений амплитуд, которые упорядочены по частоте, от нуля до половины частоты дискретизации.



Таким образом, для гармоник с частотами 100 Гц и 880 Гц, значения амплитуд, будут соответствовать тем значениям, которые были указаны в константах amplitudeOfFirstHarmonic и amplitudeOfSecondHarmonic метода someFunc(). А для остальных гармоник, которых остаётся ровно 3998, значения амплитуд будут приближены к нулю, потому что, остальные гармоники не были определены в исходной дискретной функции someFunc(), которая передавалась на дискретное преобразование Фурье.









Выведем значения амплитуд, полученных после преобразования Фурье функции someFunc(), в файл .csv и построим график в программе Excel. В результате преобразования Фурье, мы получили спектр исходного сигнала







Алгоритм дискретного преобразования Фурье на компьютере выполняется за время $inline$O(n^2)$inline$, что является слишком медленным процессом. Для быстрых вычислений дискретного преобразования Фурье, придумали быстрое преобразование Фурье (БПФ). Алгоритм БПФ, производит вычисления, используя рекурсивный подход, благодаря которому, время выполнения алгоритма уменьшается до $inline$O(n*\lg{n})$inline$. Существует готовая реализация алгоритма БПФ в среде программирования Java, которая представлена в виде библиотеки JTransforms.



Запись данных с микрофона. AudioRecord



За получения звуковых данных в формате PCM с мобильного устройства на платформе Android отвечает класс AudioRecord.

Для создания экземпляра класса AudioRecord, в конструкторе класса AudioRecord, необходимо указать такие параметры как:


  • audioSource — источник, откуда ведётся запись.

  • sampleRateInHz — частота дискретизации в Герцах.

  • channelConfig — тип аудио канала.

  • audioFormat — формат кодирования данных.

  • minBufferSize — минимальный размер буфера.



После создания экземпляра класса AudioRecord, необходимо вызвать метод startReading(), который начнёт запись с мобильного устройства. Записанные звуковые данные будут храниться во внутреннем буфере класса AudioRecord в размере порции, указанной в параметре minBufferSize при создании экземпляра класса. Из внутреннего буфера класса AudioRecord, необходимо периодически забирать записанные данные с помощью вызова метода read() класса AudioRecord.

Для примера, запишем звуковые данные с мобильного устройства, а затем выведем, эти данные, в текстовый файл .csv и построим график полученных звуковых данных, используя программу Excel





При создании экземпляра класса AudioRecord, используем частоту дискретизации равной 8000 Гц и размер буфера равный 1024.



Если эта часть статьи была интересна, то во второй части статьи мы займёмся созданием гитарного тюнера на Android.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333514/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
ferderfre

Mini разработал следящую за багажом бирку

Пятница, 14 Июля 2017 г. 15:39 (ссылка)

­­­­­­­­­­­­­­­­­­­­­­­­Марка Mini прeдставилa спeциальный комплект для путешественников из коллекции Lifestyle. Об этом коррeспондeнту «Ленты.ру» pассказал прeдставитель прeсс-службы марки.

В нeй прeдставлены чемоданы, сумки, бумажники и косметички. Модeли Overnight, Duffle и Weekender прeдназначены для нeбольших путешествий: сумки созданы из пpочного вoдоотталкивающий материалa. Компактные чемоданы с выдвижной ручкой прeдставлены двyмя модeлями: Trolley и Cabin Trolley. Внутри кейсы имеют нeсколько пpактических отсеков, крeпeжные рeмни с магнитной двyхстоpоннeй молнией.

Еще одна новинка — спeциальная бирка FindMate. Ее нужно закрeпить на сумку, а потом отслеживать положение багажа черeз спeциальное приложение в смартфонe.

Фото: прeсс-служба MINI

1/5

Также в коллекцию вoшли куртки, покрывалa, термосы и чехлы для смартфонов.

В серeдинe июня 2017 года Mini прeдставилa собственную коллекцию одeжды в pамках выставки мужской моды Pitti Uomo. Линeйка получилa название Beyond Native. В ее создании принимали участие нeсколько дизайнеpов и брeндов: Диего Ванасcибаpа, Эдвина Херль, дуэт Perret Schaad, а также марки Post-Imperial и Pronounce. Глaвная тема линeйки — гоpод и его атмосфеpа.

Комментарии (0)КомментироватьВ цитатник или сообщество
ФЛУДЕЛКА

Водителю грозит 10 тысяч штрафа за парковку машины на жидком бетоне

Вторник, 11 Июля 2017 г. 18:02 (ссылка)



Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<андроид - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda