# Кастомизация Android SDK

<Aside type="note">
Убедитесь, что вы интегрировали Pushwoosh Android SDK в ваш проект:

*   [Интеграция с Firebase](/ru/developer/pushwoosh-sdk/android-sdk/firebase-integration/quick-start/)
*   [Интеграция с Amazon](/ru/developer/pushwoosh-sdk/android-sdk/amazon/)
</Aside>

## Диплинки

В вашей activity, которая будет обрабатывать диплинк, добавьте тег `<data>` с параметрами scheme, host и pathPrefix.

```txt
<activity
          android:name=".PromoActivity"
          android:label="PromoActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="com.pushwoosh"
          android:host="promotion"
          android:pathPrefix="" />
    </intent-filter>
</activity>
```

<Aside type="note">
Имя страницы диплинка (_promotion_ в приведенном примере) указывается в поле **host**, а **не в pathPrefix**.
</Aside>

В приведенном выше примере диплинк откроет PromoActivity. Для простоты базовая реализация ниже отображает оповещение со значением promo id. В вашем приложении это, безусловно, может быть что-то более полезное!

```java
public class PromoActivity extends Activity
{
		@Override
		protected void onCreate(Bundle savedInstanceState)
		{
				super.onCreate(savedInstanceState);

				setContentView(R.layout.deep_link);
				setTitle("Deep link activity");

				Intent intent = getIntent();
	  	  String action = intent.getAction();
	    	Uri data = intent.getData();

		    if (TextUtils.equals(action, Intent.ACTION_VIEW))
		    {
	  		  	openUrl(data);
		    }
		}

		private void openUrl(Uri uri)
		{
				String promoId = uri.getQueryParameter("id");
				Toast.makeText(getApplicationContext(), promoId, Toast.LENGTH_LONG).show();
		}
}
```

## Отслеживание покупок в приложении

Если вы хотите отслеживать покупки в приложении в [Customer Journeys](/ru/product/customer-journey/pushwoosh-journey-overview), настройте отправку информации о покупках в Pushwoosh, вызвав этот метод:

```java
Pushwoosh.getInstance().sendInappPurchase(@NonNull String sku, @NonNull BigDecimal price, @NonNull String currency);
```

## Push-уведомления на основе геозон

Чтобы использовать пуши на основе геозон, добавьте библиотеку `com.pushwoosh:pushwoosh-location` и вызовите:

```java
PushwooshLocation.startLocationTracking();
```

В ваш **AndroidManifest.xml** включите необходимые разрешения:

```xml
<manifest ... >
  <!-- Required for geolocation-based push notifications -->
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

  <!-- Required for precise location tracking -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

  <!-- Required for background location access on Android 10 (API level 29) and higher -->
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>
```

## Использование локальных уведомлений с Pushwoosh

Если вы используете Pushwoosh Local Notifications API, добавьте разрешение RECEIVE\_BOOT\_COMPLETED в ваш AndroidManifest.xml:

```txt title="AndroidManifest.xml"
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>x
```

## Использование числа на значке в Android

Pushwoosh поддерживает установку числа на значке приложения для следующих лаунчеров Android:\
Sony, Samsung, LG, HTC, ASUS, ADW, APEX, NOVA, HUAWEI, ZUK, OPPO.\
Чтобы использовать эту функциональность, просто добавьте библиотеку `com.pushwoosh:pushwoosh-badge` в ваше приложение.

## Открытие кастомной activity

Если вы хотите запускать определенную activity в ответ на push-уведомления, добавьте следующий intent-filter к этой activity:

```txt title="AndroidManifest.xml"
<activity android:name="YourActivity">
    <intent-filter>
        <action android:name="${applicationId}.MESSAGE"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>
```

## Управление уровнем логирования

Для помощи в отладке и интеграции SDK по умолчанию выводит все запросы в консоль. Когда вы будете готовы к продакшн-сборке, добавьте метаданные `com.pushwoosh.log_level` со значением "ERROR" в AndroidManifest.xml. Таким образом, в консоль будет выводиться только информация об ошибках. Другими вариантами могут быть следующие:

_NONE_ - Никаких логов от SDK\
_ERROR_ - Отображать только ошибки в консоли\
_WARN_ - Отображать также предупреждения\
_INFO_ - Отображать информационные сообщения\
_DEBUG_ - Теперь отображается даже отладочная информация\
_NOISE_ - Все, что может вывести SDK, и даже больше

```txt title="AndroidManifest.xml"
<meta-data android:name="com.pushwoosh.log_level" android:value="ERROR" />
```

## Использование Proguard

При использовании Proguard добавьте следующие опции:

```txt title="proguard-rules.pro"
-keep class com.pushwoosh.** { *; }
-dontwarn com.pushwoosh.**
```

Требования библиотеки **Google Play Services** относительно Proguard смотрите здесь:\
[https://developers.google.com/android/guides/setup](https://developers.google.com/android/guides/setup)

## Кастомизация поведения при открытии уведомления

Если вам нужно программно выбирать, какую activity отображать в результате push-уведомления, вы можете создать собственный [NotificationServiceExtension](https://github.com/Pushwoosh/pushwoosh-android-sdk/blob/master/Documentation/notification/NotificationServiceExtension.md) и включить полное имя класса вашего NotificationServiceExtension в метаданные под значением `com.pushwoosh.notification_service_extension`.

```txt title="AndroidManifest.xml"
<meta-data
    android:name="com.pushwoosh.notification_service_extension"
    android:value="com.your.package.YourNotificationServiceExtension" />
```

```java title="YourNotificationServiceExtension.java"
public class YourNotificationServiceExtension extends NotificationServiceExtension {
    @Override
    protected void startActivityForPushMessage(PushMessage message) {
      // super.startActivityForPushMessage() запускает activity лаунчера по умолчанию
      // или activity, помеченную действием ${applicationId}.MESSAGE.
      // Просто не вызывайте его, чтобы переопределить это поведение.
        // super.startActivityForPushMessage(message);

        // вместо этого запустите вашу activity:
        Intent launchIntent  = new Intent(getApplicationContext(), YourActivity.class);
        launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

        // (Опционально) передать данные уведомления в Activity
        launchIntent.putExtra(Pushwoosh.PUSH_RECEIVE_EVENT, message.toJson().toString());

        context.startActivity(launchIntent);
    }
}
```

<Aside type="note">
**Важно**

Если вы используете proguard в продакшн-сборках, убедитесь, что ваш кастомный NotificationServiceExtension не обфусцирован (путем добавления правила `-keep class`), иначе это приведет к ClassNotFoundException.
</Aside>

## Кастомизация push-уведомлений

Чтобы кастомизировать вид push-уведомлений, вам нужно создать кастомную Factory. Вы можете создать кастомный [NotificationFactory](https://pushwoosh.github.io/pushwoosh-android-sdk/pushwoosh/com.pushwoosh.notification/-notification-factory/index.html) и включить полное имя класса вашей NotificationFactory в метаданные под значением `com.pushwoosh.notification_factory`.

```txt title="AndroidManifest.xml"
<meta-data
    android:name="com.pushwoosh.notification_factory"
    android:value="com.your.package.YourNotificationFactory" />
```

```java title="YourNotificationFactory"
public class YourNotificationFactory extends PushwooshNotificationFactory {
	@Override
	public Notification onGenerateNotification(@NonNull PushMessage pushMessage) {
		if (customNotification) {
       // TODO: сгенерировать и вернуть кастомное уведомление
    }

    // вернуть стандартное уведомление Pushwoosh
		return super.onGenerateNotification(pushMessage);
	}
}
```

## Кастомизация сводки группы

Чтобы кастомизировать внешний вид [сводки группы](https://developer.android.com/training/notify-user/group#set_a_group_summary), создайте кастомную Factory. Вы можете создать кастомный SummaryNotificationFactory и включить полное имя класса вашего SummaryNotificationFactory в метаданные под значением com.pushwoosh.summary\_notification\_factory.

```java title="AndroidManifest.xml"
<meta-data
    android:name="com.pushwoosh.summary_notification_factory"
    android:value="com.your.package.YourSummaryNotificationFactory" />
```

```java title="YourSummaryNotificationFactory"
public class YourSummaryNotificationFactory extends PushwooshSummaryNotificationFactory {
    @Override
    public String summaryNotificationMessage(int notificationsAmount) {
	      // вернуть желаемое сообщение
        return super.summaryNotificationMessage(notificationsAmount);
    }
    @Override
    public int summaryNotificationIconResId() {
	      // вернуть id ресурса иконки
        return super.summaryNotificationIconResId();
    }
}
```

## URL приватного эндпоинта

<Aside>
Только для подписок **Custom Plan**. За подробностями, пожалуйста, свяжитесь с нашим [отделом продаж](https://www.pushwoosh.com/demo/?utm_source=docs&utm_medium=post&utm_campaign=customizing-android-sdk).
</Aside>

Pushwoosh предоставляет приватные эндпоинты для клиентов с подписками Custom Plan. Чтобы настроить приватный эндпоинт для Android SDK, вам нужно добавить следующее в ваш файл **AndroidManifest.xml**:

```txt title="AndroidManifest.xml"
<meta-data android:name="com.pushwoosh.base_url" android:value="PUSHWOOSH_PRIVATE_ENDPOINT_URL_PROVIDED" />
```

## Создание очереди Rich Media

В случае, если необходимо одновременно отобразить несколько Rich Media страниц (например, события-триггеры для двух или более In-App срабатывают в один момент, или Rich Media страница уже отображается в момент срабатывания другого события-триггера), вы можете настроить очередь для отображения Rich Media страниц. Чтобы создать очередь, добавьте следующий код в ваш проект:

```java title="Application.java"
package com.pushwoosh.testingapp;

import com.pushwoosh.RichMediaManager;
import com.pushwoosh.exception.PushwooshException;
import com.pushwoosh.richmedia.RichMediaPresentingDelegate;
import com.pushwoosh.richmedia.RichMedia;
import com.pushwoosh.internal.utils.PWLog;

import java.util.ArrayDeque;
import java.util.concurrent.locks.ReentrantLock;

public class DefaultRichMediaPresentingDelegate implements RichMediaPresentingDelegate {
    private final String TAG = DefaultRichMediaPresentingDelegate.class.getSimpleName();
    private ArrayDeque<RichMedia> richMediaQueue = new ArrayDeque<>();
    private RichMedia currentRichMedia = null;
    private ReentrantLock reentrantLock;

    public DefaultRichMediaPresentingDelegate() {
        PWLog.noise(TAG, "new DefaultRichMediaPresentingDelegate:" + this);
        reentrantLock = new ReentrantLock();
    }

    @Override
    public boolean shouldPresent(RichMedia richMedia) {
        PWLog.noise(TAG, "shouldPresent:" + richMedia);
        if (currentRichMedia == null) {
            PWLog.noise(TAG, "currentRichMedia is null");
        }
        if (richMedia.isLockScreen()) {
            PWLog.noise(TAG, "isLockScreen is true");
            return true;
        }
        try {
            reentrantLock.lock();
            if (currentRichMedia == null) {
                PWLog.noise(TAG, "show:" + richMedia);
                currentRichMedia = richMedia;
                return true;
            } else {
                PWLog.noise(TAG, "add to queue:" + richMedia);
                richMediaQueue.add(richMedia);
                return false;
            }
        } finally {
            reentrantLock.unlock();
        }
    }

    @Override
    public void onPresent(RichMedia richMedia) {
        PWLog.noise(TAG, "onPresent" + richMedia);
    }

    @Override
    public void onError(RichMedia richMedia, PushwooshException pushwooshException) {
        PWLog.error(TAG, pushwooshException + " richMedia:"+richMedia.toString());
        tryShowNextRichMediaThreadSafety();
    }

    @Override
    public void onClose(RichMedia richMedia) {
        PWLog.noise(TAG, "onClose:" + richMedia);
        tryShowNextRichMediaThreadSafety();
    }

    private void tryShowNextRichMediaThreadSafety() {
        try {
            reentrantLock.lock();
            tryShowNextRichMedia();
        } finally {
            reentrantLock.unlock();
        }
    }

    private void tryShowNextRichMedia() {
        if (!richMediaQueue.isEmpty()) {
			currentRichMedia = richMediaQueue.poll();
			PWLog.noise(TAG, "try manual show:" + currentRichMedia);
			RichMediaManager.present(currentRichMedia);
		} else {
			PWLog.noise(TAG, "richMediaQueue is empty");
			currentRichMedia = null;
		}
    }
}
```

<Aside type="caution" title="Важно">
Мы настоятельно рекомендуем настраивать очередь в **Application**, а не в **Activity**. В противном случае может быть создано несколько очередей.
</Aside>

<Aside type="note">
Каждый вызов метода [postEvent](/ru/developer/api-reference/user-centric-api/#postevent) позволяет отобразить только один In-App, и каждый пуш может быть связан только с одним Rich Media. Если вы хотите показать несколько In-App, вызовите метод [postEvent](/ru/developer/api-reference/user-centric-api/#postevent) необходимое количество раз.
</Aside>

## Кастомный звук пуша

<Aside>
Доступно для устройств с Android 8+.
</Aside>

1.  Поместите ваш аудиофайл в соответствующую папку. Для нативного Android фреймворка ваши файлы должны быть размещены в папке `/app/src/main/res/raw`.

<Aside type="note">
Пожалуйста, обратитесь к соответствующим руководствам, чтобы узнать, куда помещать аудиофайл в проектах, созданных на других фреймворках.
</Aside>

2.  Создайте [канал уведомлений.](/ru/developer/pushwoosh-sdk/android-sdk/notification-channels/)

3.  Выберите звук при настройке push-сообщения.

<img src="/android-push-notifications-customizing-android-sdk-5.0-1.webp" alt=""/>

4.  Установите канал уведомлений, к которому будет принадлежать сообщение. Для этого укажите следующее в поле “Android root params”: `{"pw_channel": "PUSH NOTIFICATION CHANNEL NAME"} //` `_`здесь вам нужно указать имя вашего канала с кастомным звуком`_`

В случае использования remote API, установите параметры следующим образом в вашем /createMessage API запросе:

```java
"android_root_params": {"pw_channel": "push"} // здесь вам нужно указать имя вашего канала с кастомным звуком, например, "push" для уведомлений со звуком push.wav.
"android_sound": "push" // здесь вы должны указать имя файла без расширения
```

Как только вы отправите пуш с указанными параметрами, канал уведомлений с выбранным звуком будет создан для всех устройств с Android 8+.

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

### Правила Proguard для кастомных звуков уведомлений

Если ваше приложение использует proguard для сжатия кода и ресурсов, важно сохранить ваши звуковые файлы нетронутыми и доступными для внешних библиотек. Если вы используете свойство **`minifyEnabled = true`** в вашем **build.gradle,** добавьте следующие правила в ваш **proguard-rules.pro**:

```
-keep public class your.package.name.R$raw {
 *;
}
```

Если вы сжимаете ресурсы вашего приложения поверх сжатия кода, используя свойство **`shrinkResources=true`**, вам следует дополнительно указать, какие ресурсы вы хотите сохранить. Для этого создайте новый XML-файл с любым именем, сохраните его где-нибудь в вашем проекте (например, в res/xml) и укажите имена ресурсов под параметром **`tools:keep`** в теге **`resources`**:

```
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
 tools:keep="@raw/*"
/>
```

## Полный список флагов метаданных Android SDK

Чтобы установить флаг, вам нужно добавить блок метаданных в ваш файл **AndroidManifest.xml** внутри тега **application**. Например, если вы хотите установить ID приложения Pushwoosh, добавьте следующий код в ваш файл **AndroidManifest.xml**:

```txt title="AndroidManifest.xml"
<meta-data
    android:name="com.pushwoosh.appid"
    android:value="XXXXX-XXXXX" />
```

<table><thead><tr><th width="262.33243208828077" align="center">Флаг</th><th width="284" align="center">Описание</th><th align="center">Возможные значения</th></tr></thead><tbody><tr><td align="center">com.pushwoosh.appid</td><td align="center">Устанавливает ID приложения Pushwoosh.</td><td align="center">XXXXX-XXXXX</td></tr><tr><td align="center">com.pushwoosh.log_level</td><td align="center">Устанавливает уровень логирования. Подробнее см. в разделе <a href="#upravlenie-urovnem-logirovaniya">Управление уровнем логирования</a>. </td><td align="center">NONE / ERROR / WARN / INFO / <strong>DEBUG</strong> (<em>по умолчанию</em>) / NOISE</td></tr><tr><td align="center">com.pushwoosh.base_url</td><td align="center">Переопределяет базовый URL сервера Pushwoosh.</td><td align="center"><a href="https://cp.pushwoosh.com/json/1.3/">https://cp.pushwoosh.com/json/1.3/</a> (<em>по умолчанию</em>)</td></tr><tr><td align="center">com.pushwoosh.notification_service_extension</td><td align="center">Кастомный NotificationServiceExtension. Подробнее см. в разделе <a href="#kastimizatsiya-povedeniya-pri-otkrytii-uvedomleniya">Кастомизация поведения при открытии уведомления</a>.  </td><td align="center">com.myapp.MyNotificationServiceExtension</td></tr><tr><td align="center">com.pushwoosh.notification_factory</td><td align="center"><p>Кастомный NotificationFactory.</p><p>Подробнее см. в разделе <a href="#kastimizatsiya-push-uvedomleniy">Кастомизация push-уведомлений</a>. </p></td><td align="center">com.myapp.MyNotificationFactory</td></tr><tr><td align="center">com.pushwoosh.summary_notification_factory</td><td align="center">Кастомный SummaryNotificationFactory.</td><td align="center">com.myapp.MySummaryNotificationFactory</td></tr><tr><td align="center">com.pushwoosh.multi_notification_mode</td><td align="center">Если true, уведомления будут сгруппированы. Если false, будет отображаться только последнее полученное уведомление.</td><td align="center">true / <strong>false</strong> (<em>по умолчанию</em>)</td></tr><tr><td align="center">com.pushwoosh.allow_server_communication</td><td align="center">Если true, SDK разрешено отправлять сетевые запросы на серверы Pushwoosh.</td><td align="center"><strong>true</strong> (<em>по умолчанию</em>) / false</td></tr><tr><td align="center">com.pushwoosh.handle_notifications_using_workmanager</td><td align="center">Если true, для обработки уведомлений используется WorkManager.</td><td align="center">true / <strong>false</strong> (<em>по умолчанию</em>)</td></tr><tr><td align="center">com.pushwoosh.notification_icon</td><td align="center">Имя ресурса кастомной (маленькой) иконки уведомления. Если null, будет использоваться иконка приложения по умолчанию. </td><td align="center">res/drawable-xxhdpi-v11/notification_small_icon.png / null</td></tr><tr><td align="center">com.pushwoosh.notification_icon_color</td><td align="center">Цвет фона (маленькой) иконки уведомления.</td><td align="center">#FFFFFF</td></tr><tr><td align="center">com.pushwoosh.allow_collecting_device_data</td><td align="center">Если true, SDK разрешено собирать и отправлять данные устройства в Pushwoosh.</td><td align="center"><strong>true</strong> (<em>по умолчанию</em>) / false</td></tr><tr><td align="center">com.pushwoosh.allow_collecting_device_os_version</td><td align="center">Если true, SDK разрешено собирать и отправлять версию ОС устройства в Pushwoosh.</td><td align="center"><strong>true</strong> (<em>по умолчанию</em>) / false</td></tr><tr><td align="center">com.pushwoosh.allow_collecting_device_locale</td><td align="center">Если true, SDK разрешено собирать и отправлять локаль устройства в Pushwoosh.</td><td align="center"><strong>true</strong> (<em>по умолчанию</em>) / false</td></tr><tr><td align="center">com.pushwoosh.allow_collecting_device_model</td><td align="center">Если true, SDK разрешено собирать и отправлять модель устройства в Pushwoosh.</td><td align="center"><strong>true</strong> (<em>по умолчанию</em>) / false</td></tr><tr><td align="center">com.pushwoosh.in_app_business_solutions_capping</td><td align="center">Ограничивает количество показов In-App <em>push-unregister</em> в день.</td><td align="center"><strong>1</strong> (<em>по умолчанию</em>), 2, ..., n</td></tr><tr><td align="center">com.pushwoosh.start_foreground_service</td><td align="center">Если true, Foreground Service запускается вместе с вызовом PushwooshLocation.startLocationTracking()</td><td align="center">true / <strong>false</strong> (<em>по умолчанию</em>)</td></tr><tr><td align="center">com.pushwoosh.foreground_service_notification_text</td><td align="center">Устанавливает текст уведомления, создаваемого при запуске Foreground Service для ключа “com.pushwoosh.start_foreground_service”.</td><td align="center"><strong>Work in progress</strong> (<em>по умолчанию</em>)</td></tr><tr><td align="center">com.pushwoosh.foreground_service_notification_channel_name</td><td align="center">Устанавливает имя канала для уведомления, создаваемого при запуске Foreground Service для ключа “com.pushwoosh.start_foreground_service”. </td><td align="center"><strong>Foreground service</strong> (<em>по умолчанию)</em></td></tr><tr><td align="center">com.pushwoosh.trusted_package_names</td><td align="center">Позволяет делиться Pushwoosh HWID с указанным пакетом</td><td align="center">"com.mycompany.myapp1, com.mycompany.myapp2"</td></tr></tbody></table>

## Удаление Push-уведомлений через TTL (Time-To-Live)

Чтобы автоматически удалять push-уведомления по истечении указанного периода времени с помощью TTL (Time-to-Live), выполните следующие шаги:

1.  Создайте кастомный NotificationFactory. [Узнать больше](#kastimizatsiya-push-uvedomleniy)

2.  В методе `onGenerateNotification()` создайте уведомление с помощью класса `Notification.Builder` или `NotificationCompat.Builder` и вызовите метод `setTimeoutAfter`:

```java
public class YourNotificationFactory extends PushwooshNotificationFactory {

    @Override
    public Notification onGenerateNotification(@NonNull PushMessage pushMessage) {
        Notification.Builder builder = new Notification.Builder(getApplicationContext(), addChannel(pushData));

        Notification notification = builder.setContentText(pushData.getMessage())
                                           .setContentTitle(title)
                                           .setContentText(text)
                                           // rest of your notification creation code
                                           .setTimeoutAfter(timeout) // время в миллисекундах, по истечении которого уведомление будет отменено
                                           .build();
    }
}

```

## Поделитесь с нами вашим мнением

Ваши отзывы помогают нам улучшать наш продукт, поэтому мы будем рады, если вы поделитесь с нами любыми проблемами, возникшими в процессе интеграции SDK. Если вы столкнетесь с какими-либо трудностями, пожалуйста, не стесняйтесь поделиться своими мыслями с нами через [эту форму](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform).