Расположение макета Android на устройстве?


147

Как я могу смоделировать свое местоположение на физическом устройстве (Nexus One)? Я знаю, что вы можете сделать это с эмулятором на панели управления эмулятором, но это не работает для физического устройства.


Для настольных компьютеров вы можете создавать / устанавливать драйверы, которые имитируют устройство GPS / устройство с последовательным портом с целью проведения такого рода тестирования. Возможно, вы могли бы создать или найти что-то подобное для Android.
— AaronLS

6
В Маркете есть несколько приложений, которые могут эмулировать местоположение GPS. Например "Расположение спуфер".
— Влад

В магазине есть множество приложений, которые делают это .. Вы можете попробовать это - play.google.com/store/apps/… или это - play.google.com/store/apps/…
— Yehuda

1
В магазине также есть приложение, которое поддерживает команды "geo fix" и "geo nmea", как эмулятор! play.google.com/store/apps/...
— Luv

@Iuv более того, это с открытым исходным кодом github.com/luv/mockgeofix
— Влад

Ответы:


166

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

Вы должны включить макеты мест на панели разработки в настройках и добавить

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

к вашему манифесту.

Теперь вы можете зайти в свой код и создать своего собственного провайдера для определения местоположения и указать местоположение этого провайдера.


83
На рынке есть приложение под названием «Fake GPS». Если вы включите Mock Locations на вашем устройстве, он предлагает хороший графический интерфейс для установки вашего местоположения. Я понимаю, что это не самостоятельное решение, а, безусловно, достойное решение для быстрого тестирования.
— Тим Грин

2
это может быть ссылка - та же дата и соответствующий контент.
— DefenestrationDay

4
@TimGreen Ваш ответ должен быть принят (а не только комментарий)! Безусловно самый простой способ проверить
— Бостоне

На самом деле, приложение «поддельный GPS» не является лучшим, потому что оно может только устанавливать местоположения и случайным образом перемещаться. Их новая версия может проложить маршрут, но она перебирает только путевые точки, поэтому огромные скачки в локации. Ответ Януша действительно верен, что вам нужно создать своего собственного провайдера. Мы недавно сделали это, и если кому-то это понадобится или если этот вопрос все еще нуждается в помощи, я выложу код.
— vkinra

5
Утверждение «зайди в свой код и создай свой собственный провайдер фиктивного местоположения» должно быть объяснено более подробно.
— Dims

102

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


68
Почему ненавистники? Этот ответ является очень сложным.
— Бен Зотто

61
Потрясающе хардкорный, хотя и совершенно бесполезный / не относящийся к делу.
— olivervbk

13
Достаточно весело, хотя.
— Этьен

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

10
Если у вас возникли проблемы с выяснением того, как генерировать реалистичные последовательности NMEA в ваше распаянное устройство, есть замечательный вариант: припаять туда реальное внешнее устройство GPS и переместить всю лабораторию разработки на реальный трек, на вершину команды бегунов / байкеров / автомобили / самолеты / что угодно в зависимости от скорости, которую вы хотите имитировать. Не забудьте открыть отверстие в крыше лаборатории разработки, чтобы устройство могло принимать радиосигналы со спутников. Пожалуйста. ;-)
— Стефан Гурихон

32

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

$ telnet localhost 5554
Android Console: type 'help' for a list of commands
OK
geo fix -82.411629 28.054553
OK

Я не могу вспомнить, если вы можете telnet к вашему устройству, но я думаю, что вы можете. Надеюсь, это поможет.

Для этого вам понадобится adb (мост отладки Android) (CLI).


вы получаете отказ в доступе ко всему, что отправляете в виде команды на консоль вашего устройства. Может быть, это возможно с терминальным приложением, но я думаю, что требуется root-доступ
— Janusz

Просто мы на одной странице: вы не говорите о "оболочке adb"? То, о чем я говорю, отличается, это что-то, что позволяет вам устанавливать специфичные для Android параметры. Я знаю, например, что вы можете настроить переадресацию портов (т. Е. Localhost: 1234 идет на телефон: 1234) без корневого доступа, поэтому вы можете настроить прокси-сервер SOCKS и туннель через кабель без root.
— Тим Грин

Вы можете использовать adb forward tcp: 6000 tcp: 23 для перенаправления порта 6000 на вашей машине на порт 23 на устройстве, но я не смог подключиться, даже если порт отображается как открытый. Использование telnet-клиента на устройстве и подключение к localhost также не удается
— Janusz

15
Это хорошо работает для эмулятора, но не работает для реального устройства. Так что это не отвечает на вопрос ОП вообще :(
— Мэтью Гиллиард

4
@TimGreen Ваше решение только для эмулятора. Вопрос был о реальном устройстве.
— Игорь Ганапольский

29

Вы можете использовать разрешение Службы определения местоположения, чтобы смоделировать местоположение ...

"android.permission.ACCESS_MOCK_LOCATION"

а затем в вашем коде Java,

// Set location by setting the latitude, longitude and may be the altitude...
String[] MockLoc = str.split(",");
Location location = new Location(mocLocationProvider);            
Double lat = Double.valueOf(MockLoc[0]);
location.setLatitude(lat);
Double longi = Double.valueOf(MockLoc[1]);
location.setLongitude(longi);
Double alti = Double.valueOf(MockLoc[2]);
location.setAltitude(alti);

9
что в значении mocLocationProvider?
— Виктор

например, LocationManager.GPS_PROVIDER
— enigma969

25

Я имел успех со следующим кодом. Хотя по какой-то причине у меня была одна блокировка (даже если я пробовал разные пары LatLng), это сработало для меня. mLocationManagerэто LocationManagerкоторый подключен к LocationListener:

private void getMockLocation()
{
    mLocationManager.removeTestProvider(LocationManager.GPS_PROVIDER);
    mLocationManager.addTestProvider
    (
      LocationManager.GPS_PROVIDER,
      "requiresNetwork" == "",
      "requiresSatellite" == "",
      "requiresCell" == "",
      "hasMonetaryCost" == "",
      "supportsAltitude" == "",
      "supportsSpeed" == "",
      "supportsBearing" == "",

      android.location.Criteria.POWER_LOW,
      android.location.Criteria.ACCURACY_FINE
    );      

    Location newLocation = new Location(LocationManager.GPS_PROVIDER);

    newLocation.setLatitude (/* TODO: Set Some Lat */);
    newLocation.setLongitude(/* TODO: Set Some Lng */);

    newLocation.setAccuracy(500);

    mLocationManager.setTestProviderEnabled
    (
      LocationManager.GPS_PROVIDER, 
      true
    );

    mLocationManager.setTestProviderStatus
    (
       LocationManager.GPS_PROVIDER,
       LocationProvider.AVAILABLE,
       null,
       System.currentTimeMillis()
    );      

    mLocationManager.setTestProviderLocation
    (
      LocationManager.GPS_PROVIDER, 
      newLocation
    );      
}

1
Хороший пост, см. Мой ответ ниже для дополнений.
— icyerasor

Насколько я помню, setTestProviderStatusздесь используется для уведомления / публикации / планирования события с LocationManager о том, что провайдер GPS обнаружил исправление «сейчас», чтобы могла произойти обработка указанного события.
— Dr1Ku

Что значит « mLocationManagerэто , LocationManagerкоторый подключен к LocationListener»? как я могу это сделать? Спасибо ..
— Иегуда

Вот одна статья, объясняющая, как проверить изменение местоположения с помощью LocationManagerсервиса и LocationListener: blog.doityourselfandroid.com/2010/12/25/…
— Dr1Ku

2
плюс 1 за то, как вы представляли true (s) и false (s) в методе addTestProvider
— A.Alqadomi

16

На что Dr1Ku выложил работы. Использовал код сегодня, но нужно было добавить больше locs. Итак, вот некоторые улучшения:

Необязательно: Вместо использования строки LocationManager.GPS_PROVIDER может потребоваться определить собственное constat PROVIDER_NAME и использовать его. При регистрации для получения обновлений местоположения выберите поставщика с помощью критериев, а не указывайте его в виде строки.

Во-первых: вместо вызова removeTestProvider, сначала проверьте, есть ли провайдер, которого нужно удалить (чтобы избежать IllegalArgumentException):

if (mLocationManager.getProvider(PROVIDER_NAME) != null) {
  mLocationManager.removeTestProvider(PROVIDER_NAME);
}

Второе: чтобы опубликовать более одного местоположения, вы должны установить время для местоположения:

newLocation.setTime(System.currentTimeMillis());
...
mLocationManager.setTestProviderLocation(PROVIDER_NAME, newLocation);

Также, похоже, есть тест Google, который использует MockLocationProviders: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/location/LocationManagerProximityTest.java

Еще один хороший рабочий пример можно найти по адресу: http://pedroassuncao.com/blog/2009/11/12/android-location-provider-mock/

Еще одна хорошая статья: http://ballardhack.wordpress.com/2010/09/23/location-gps-and-automated-testing-on-android/#comment-1358 Вы также найдете некоторый код, который действительно работает для меня на эмуляторе.


Классное предложение по определению вашего собственного провайдера. С помощью GPS_PROVIDER вы, по-видимому, не можете избавиться от фиктивного провайдера (точнее, вы не можете переключиться на обычный GPS) до перезагрузки. Отличный пост, я проверю ссылки!
— Dr1Ku

@ Dr1KU Неужели вы не можете вернуться к обычному GPS? Это указано где-то?
— Каз Дракон

@KazDragon Так было тогда. Из того, что я могу вспомнить, после включения Mock Providerнормальный GPS не работал, я не мог получить исправление (я тестировал непосредственно на устройстве). Это может быть не так, поскольку с тех пор в Android много чего произошло.
— Dr1Ku

@ Dr1Ku А, ну, сейчас я делаю нечто очень похожее и получаю результаты, аналогичные тем, которые вы описали. Я надеялся узнать, было ли это ограничение, встроенное в платформу, или я просто где-то пропускал вызов функции. Я, наверное, должен сделать из этого вопрос.
— Kaz Dragon

9

В Android Market есть приложения, которые позволяют вам указать «Местоположение GPS-макета» для вашего устройства.

Я искал https://market.android.com и нашел приложение под названием «Мое поддельное местоположение», которое работает для меня.

Поставщик Mock GPS, упомянутый Полом выше (по адресу http://www.cowlumbus.nl/forum/MockGpsProvider.zip ), является еще одним примером, включающим исходный код - хотя я не смог установить прилагаемый APK (он говорит Failure [INSTALL_FAILED_OLDER_SDK] и может понадобиться перекомпиляция)

Для того, чтобы использовать макеты местоположений GPS, вы должны включить его в настройках вашего устройства. Зайдите в Настройки -> Приложения -> Разработка и проверьте «Разрешить фиктивные местоположения»

Затем вы можете использовать приложение, подобное описанному выше, для установки координат GPS, а карты Google и другие приложения будут использовать фиктивное местоположение GPS, которое вы укажете.


Поддельный GPS проще. Мне просто интересно, почему они спрашивают идентификатор вызывающего абонента в разрешении приложения: S
— Гийом Масс

8

Это сработало для меня (Android Studio):

Отключить GPS и WiFi отслеживание на телефоне. На Android 5.1.1 и ниже выберите «Включить макет местоположения» в разделе «Параметры разработчика».

Сделайте копию своего манифеста в каталоге src / debug. Добавьте к нему следующее (за пределами тега «application»):

использует-разрешение android: name = "android.permission.ACCESS_MOCK_LOCATION"

Настройте фрагмент карты под названием «карта». Включите следующий код в onCreate ():

lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
ll = new MyLocationListener();
if (lm.getProvider("Test") == null) {
    lm.addTestProvider("Test", false, false, false, false, false, false, false, 0, 1);
}
lm.setTestProviderEnabled("Test", true);
lm.requestLocationUpdates("Test", 0, 0, ll);

map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
    @Override
    public void onMapClick(LatLng l) {
        Location loc = new Location("Test");
        loc.setLatitude(l.latitude);
        loc.setLongitude(l.longitude);
        loc.setAltitude(0); 
        loc.setAccuracy(10f);
        loc.setElapsedRealtimeNanos(System.nanoTime());
        loc.setTime(System.currentTimeMillis()); 
        lm.setTestProviderLocation("Test", loc);
    }
};

Обратите внимание, что вам, возможно, придется временно увеличить minSdkVersion в файле Gradle вашего модуля до 17, чтобы использовать метод setElapsedRealtimeNanos.

Включите следующий код в основной класс деятельности:

private class MyLocationListener implements LocationListener {
    @Override
    public void onLocationChanged(Location location) {
        // do whatever you want, scroll the map, etc.
    }
}

Запустите ваше приложение с AS. На Android 6.0 и выше вы получите исключение безопасности. Теперь перейдите в «Параметры разработчика» в «Настройках» и выберите «Выбор приложения для определения местоположения». Выберите ваше приложение из списка.

Теперь, когда вы нажимаете на карту, onLocationChanged () будет срабатывать с координатами вашего касания.

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


я думаю, что это работает на Android 6, если вы
— Хамза

7

Я создал простой обработчик, имитирующий перемещение позиции из начальной позиции.

Запустите его в обратном вызове вашего соединения:

private final GoogleApiClient.ConnectionCallbacks mConnectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
    @Override
    public void onConnected(Bundle bundle) {
        if (BuildConfig.USE_MOCK_LOCATION) {
            LocationServices.FusedLocationApi.setMockMode(mGoogleApiClient, true);
            new MockLocationMovingHandler(mGoogleApiClient).start(48.873399, 2.342911);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {

    }
};

Класс Handler:

   private static class MockLocationMovingHandler extends Handler {

    private final static int SET_MOCK_LOCATION = 0x000001;
    private final static double STEP_LATITUDE =  -0.00005;
    private final static double STEP_LONGITUDE = 0.00002;
    private final static long FREQUENCY_MS = 1000;
    private GoogleApiClient mGoogleApiClient;
    private double mLatitude;
    private double mLongitude;

    public MockLocationMovingHandler(final GoogleApiClient googleApiClient) {
        super(Looper.getMainLooper());
        mGoogleApiClient = googleApiClient;
    }

    public void start(final double initLatitude, final double initLongitude) {
        if (hasMessages(SET_MOCK_LOCATION)) {
            removeMessages(SET_MOCK_LOCATION);
        }
        mLatitude = initLatitude;
        mLongitude = initLongitude;
        sendEmptyMessage(SET_MOCK_LOCATION);
    }

    public void stop() {
        if (hasMessages(SET_MOCK_LOCATION)) {
            removeMessages(SET_MOCK_LOCATION);
        }
    }

    @Override
    public void handleMessage(Message message) {
        switch (message.what) {
            case SET_MOCK_LOCATION:
                Location location = new Location("network");
                location.setLatitude(mLatitude);
                location.setLongitude(mLongitude);
                location.setTime(System.currentTimeMillis());
                location.setAccuracy(3.0f);
                location.setElapsedRealtimeNanos(System.nanoTime());
                LocationServices.FusedLocationApi.setMockLocation(mGoogleApiClient, location);

                mLatitude += STEP_LATITUDE;
                mLongitude += STEP_LONGITUDE;
                sendEmptyMessageDelayed(SET_MOCK_LOCATION, FREQUENCY_MS);
                break;
        }
    }
}

Надеюсь, что это может помочь ..


6

Решение, упомянутое icyerasor и предоставленное Педро по адресу http://pedroassuncao.com/blog/2009/11/12/android-location-provider-mock/, сработало для меня очень хорошо. Тем не менее, он не предлагает поддержку для правильного запуска, остановки и перезапуска провайдера фиктивного GPS.

Я немного изменил его код и переписал класс, чтобы он стал AsyncTask вместо Thread. Это позволяет нам взаимодействовать с потоком пользовательского интерфейса, поэтому мы можем перезапустить провайдера с того места, где мы были, когда мы его остановили. Это удобно, когда меняется ориентация экрана.

Код вместе с примером проекта для Eclipse можно найти на GitHub: https://github.com/paulhoux/Android-MockProviderGPS.

Вся заслуга должна пойти на Педро за большую часть тяжелой работы.


6

Добавить в свой манифест

<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
    tools:ignore="MockLocation,ProtectedPermissions" />

Функция фиктивного местоположения

void setMockLocation() {
    LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
    locationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, false,
            false, false, true, true, true, 0, 5);
    locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);

    Location mockLocation = new Location(LocationManager.GPS_PROVIDER);
    mockLocation.setLatitude(-33.852);  // Sydney
    mockLocation.setLongitude(151.211);
    mockLocation.setAltitude(10);
    mockLocation.setAccuracy(5);
    mockLocation.setTime(System.currentTimeMillis());
    mockLocation.setElapsedRealtimeNanos(System.nanoTime());
    locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, mockLocation);
}

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


3

Поддельное приложение GPS от Google Play помогло мне. Просто убедитесь, что вы прочитали все указания в описании приложения. Вы должны отключить другие службы определения местоположения, а также запустить свое приложение после включения «Fake GPS». Отлично работал для того, что мне нужно.

Вот ссылка на приложение в GooglePlay: Поддельный GPS



2

Установите приложение Fake GPS https://play.google.com/store/apps/details?id=com.incorporateapps.fakegps.fre&hl=en.

Параметры разработчика -> Выбрать приложение для определения местоположения (это означает, что выбрано приложение с поддельным местоположением).

Поддельное приложение GPS :

Двойную вкладку на карте, чтобы добавить -> нажмите кнопку воспроизведения -> Показать тост "Поддельное местоположение остановлено"

наконец, проверьте с помощью приложений Google Map.


2

Я написал приложение, которое запускает WebServer (REST-Like) на вашем телефоне Android, чтобы вы могли удаленно устанавливать GPS-положение . Веб-сайт предоставляет карту, на которой вы можете щелкнуть, чтобы установить новую позицию, или использовать клавиши «wasd» для перемещения в любом направлении. Приложение было быстрым решением, поэтому почти нет ни пользовательского интерфейса, ни документации, но реализация проста, и вы можете посмотреть все в (только четырех) классах.

Репозиторий проекта: https://github.com/juliusmh/RemoteGeoFix


1

Если ваше устройство подключено к вашему компьютеру и вы пытаетесь изменить отправку GPS-шнуров с помощью эмулятора, это не сработает.
Это ЭМУЛЯТОР управления по причине.
Просто установите его, чтобы сообщить вам об изменении GPS.

lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);  

    ll = new LocationListener() {        
        public void onLocationChanged(Location location) {  
          // Called when a new location is found by the network location provider.  
            onGPSLocationChanged(location); 
        }

        public void onStatusChanged(String provider, int status, Bundle extras) {       
            bigInfo.setText("Changed "+ status);  
        }

        public void onProviderEnabled(String provider) {
            bigInfo.setText("Enabled "+ provider);
        }

        public void onProviderDisabled(String provider) {
            bigInfo.setText("Disabled "+ provider);
        }
      };

Когда GPS обновится, перепишите следующий метод, чтобы сделать то, что вы хотите;

public void onGPSLocationChanged(Location location){  
if(location != null){  
    double pLong = location.getLongitude();  
    double pLat = location.getLatitude();  
    textLat.setText(Double.toString(pLat));  
    textLong.setText(Double.toString(pLong));  
    if(autoSave){  
        saveGPS();  
        }
    }
}

Не забудьте поместить их в манифест
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_MOCK_LOCATION


0

Интересно, нужна ли вам сложная настройка Mock Location. В моем случае, как только я получил фиксированное местоположение, я вызывал функцию, чтобы сделать что-то с этим новым местоположением. В таймере создайте макет локации. И вместо этого вызовите функцию с этим местоположением. Зная все это в скором времени, GPS определит реальное текущее местоположение. Что в порядке. Если у вас установлено достаточно большое время обновления.


0

Возможно, это не «программистский» подход, но если вы хотите сэкономить свое время и мгновенно получить рабочее решение, попробуйте одно из приложений, предназначенных для определения местоположения, доступных в Google Play:

Поддельный GPS Location Spoofer

Ложные Места

Поддельное местоположение GPS


1
Ты пробовал их? Работали ли они так, как было задано в вопросе?
— ареколек

Любые предпочтения? Или все они нужны? : P
— arekolek

Поддельный GPS Location Spoofer достаточно хорош, но Mock Locations позволяет установить маршрут, что является хорошей особенностью.
— климат

0

Воспользуйтесь очень удобным и бесплатным интерактивным симулятором местоположения для телефонов и планшетов с ОС Android (названным CATLES). Он проверяет местоположение GPS на уровне всей системы (даже в приложениях Google Maps или Facebook) и работает как на физических, так и на виртуальных устройствах:

Веб-сайт: http://ubicom.snet.tu-berlin.de/catles/index.html

Видео: https://www.youtube.com/watch?v=0WSwH5gK7yg


0

Используйте это разрешение в файле манифеста

<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION">

android studio рекомендует, чтобы «Макетное местоположение запрашивалось только в тестовом или отладочном файле манифеста (обычно src / debug / AndroidManifest.xml)», просто отключите проверку

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

Используйте LocationManager

locationManager.addTestProvider(mocLocationProvider, false, false,
                false, false, true, true, true, 0, 5);
locationManager.setTestProviderEnabled(mocLocationProvider, true);

Теперь установите местоположение, где вы хотите

Location mockLocation = new Location(mocLocationProvider); 
mockLocation.setLatitude(lat); 
mockLocation.setLongitude(lng); 
mockLocation.setAltitude(alt); 
mockLocation.setTime(System.currentTimeMillis()); 
locationManager.setTestProviderLocation( mocLocationProvider, mockLocation); 

0

Вышеуказанные решения не работали для меня, потому что я тестировал на устройстве Android с последней версией Сервисов Google Play, которая использует FusedLocationProviderClient . После установки разрешения на фиктивное местоположение в манифесте приложения и в качестве указанного приложения для фиктивного местоположения в настройках разработчика (как упомянуто в предыдущих ответах) я добавил код Kotlin, приведенный ниже, который успешно смоделировал местоположение.

locationProvider = FusedLocationProviderClient(context)
locationProvider.setMockMode(true)

val loc = Location(providerName)
val mockLocation = Location(providerName) // a string
mockLocation.latitude = latitude  // double
mockLocation.longitude = longitude
mockLocation.altitude = loc.altitude
mockLocation.time = System.currentTimeMillis()
mockLocation.accuracy = 1f
mockLocation.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    mockLocation.bearingAccuracyDegrees = 0.1f
    mockLocation.verticalAccuracyMeters = 0.1f
    mockLocation.speedAccuracyMetersPerSecond = 0.01f
}
//        locationManager.setTestProviderLocation(providerName, mockLocation)
locationProvider.setMockLocation(mockLocation)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.