# 自定义 Android SDK

<Aside type="note">
请确保您已将 Pushwoosh Android SDK 集成到您的项目中：

*   [Firebase 集成](/zh/developer/pushwoosh-sdk/android-sdk/firebase-integration/quick-start/)
*   [Amazon 集成](/zh/developer/pushwoosh-sdk/android-sdk/amazon/)
</Aside>

## 深层链接

在将处理深层链接的 activity 中，添加带有 scheme、host 和 pathPrefix 参数的 \<data> 标签。

```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](/zh/product/customer-journey/pushwoosh-journey-overview) 中跟踪应用内购买，请通过调用此方法配置向 Pushwoosh 发送购买信息：

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

## Geozones 推送通知

要使用地理区域推送，请添加 `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 本地通知 API，请将 RECEIVE\_BOOT\_COMPLETED 权限添加到您的 AndroidManifest.xml 中：

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

## 在 Android 上使用角标数字

Pushwoosh 支持在以下 Android 启动器的应用图标快捷方式上设置角标数字：\
索尼、三星、LG、HTC、华硕、ADW、APEX、NOVA、华为、ZUK、OPPO。\
要使用此功能，只需将 `com.pushwoosh:pushwoosh-badge` 库添加到您的应用程序中即可。

## 打开自定义 activity

如果您想在响应推送通知时启动特定的 activity，请将以下 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 默认会将所有请求打印到控制台。当您准备好进行生产构建时，请将值为 "ERROR" 的 `com.pushwoosh.log_level` meta-data 添加到 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.**
```

有关 Proguard 的 **Google Play Services** 库要求，请参阅：\
[https://developers.google.com/android/guides/setup](https://developers.google.com/android/guides/setup)

## 自定义通知打开行为

如果您需要以编程方式选择因推送通知而显示的 activity，您可以创建自定义的 [NotificationServiceExtension](https://github.com/Pushwoosh/pushwoosh-android-sdk/blob/master/Documentation/notification/NotificationServiceExtension.md)，并在 `com.pushwoosh.notification_service_extension` 值的元数据中包含您的 NotificationServiceExtension 的完全限定类名。

```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() starts default launcher activity
      // or activity marked with ${applicationId}.MESSAGE action.
      // Simply do not call it to override this behaviour.
        // super.startActivityForPushMessage(message);

        // start your activity instead:
        Intent launchIntent  = new Intent(getApplicationContext(), YourActivity.class);
        launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

        // (Optional) pass notification data to Activity
        launchIntent.putExtra(Pushwoosh.PUSH_RECEIVE_EVENT, message.toJson().toString());

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

<Aside type="note">
**重要**

如果您在生产构建中使用 proguard，请确保您的自定义 NotificationServiceExtension 没有被混淆（通过添加 `-keep class` 规则），否则将导致 ClassNotFoundException。
</Aside>

## 自定义推送通知

要自定义推送通知的视图，您需要创建一个自定义 Factory，您可以创建自定义的 [NotificationFactory](https://pushwoosh.github.io/pushwoosh-android-sdk/pushwoosh/com.pushwoosh.notification/-notification-factory/index.html)，并在 `com.pushwoosh.notification_factory` 值的元数据中包含您的 NotificationFactory 的完全限定类名。

```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: generate and return custom notification
    }

    // return default Pushwoosh notification
		return super.onGenerateNotification(pushMessage);
	}
}
```

## 自定义分组摘要

要自定义[分组摘要](https://developer.android.com/training/notify-user/group#set_a_group_summary)的外观，请创建一个自定义 Factory。您可以创建自定义的 SummaryNotificationFactory，并在 com.pushwoosh.summary\_notification\_factory 值的元数据中包含您的 SummaryNotificationFactory 的完全限定类名。

```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 the message you want
        return super.summaryNotificationMessage(notificationsAmount);
    }
    @Override
    public int summaryNotificationIconResId() {
	      // return the icon resource id you want
        return super.summaryNotificationIconResId();
    }
}
```

## 私有端点 URL

<Aside>
仅适用于**自定义计划**订阅。有关更多详细信息，请联系我们的[销售团队](https://www.pushwoosh.com/demo/?utm_source=docs&utm_medium=post&utm_campaign=customizing-android-sdk)。
</Aside>

Pushwoosh 为拥有自定义计划订阅的客户提供私有端点。要为 Android SDK 设置私有端点，您需要将以下内容添加到您的 **AndroidManifest.xml** 文件中：

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

## 创建富媒体队列

如果需要同时显示多个富媒体页面（例如，两个或多个应用内消息的触发事件同时发生，或者在另一个触发事件发生时已经显示了一个富媒体页面），您可以设置一个富媒体页面显示队列。要创建队列，请将以下代码添加到您的项目中：

```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](/zh/developer/api-reference/user-centric-api/#postevent) 方法只允许显示一个应用内消息，并且每个推送只能与一个富媒体关联。如果您想显示多个应用内消息，请根据需要多次调用 [postEvent](/zh/developer/api-reference/user-centric-api/#postevent) 方法。
</Aside>

## 自定义声音推送

<Aside>
适用于 Android 8+ 设备。
</Aside>

1.  将您的音频文件放在正确的文件夹中。对于原生 Android 框架，您的文件应放在 `/app/src/main/res/raw` 文件夹中。

<Aside type="note">
请参考相应指南，了解在其他框架构建的项目中应将音频文件放在何处。
</Aside>

2.  创建一个[通知渠道](/zh/developer/pushwoosh-sdk/android-sdk/notification-channels/)。

3.  在配置推送消息时选择一个声音。

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

4.  设置消息所属的通知渠道。为此，请在“Android root params”字段中指定以下内容：`{"pw_channel": "PUSH NOTIFICATION CHANNEL NAME"} //` `_`此处需要指定带有自定义声音的渠道名称`_`

如果使用远程 API，请在您的 /createMessage API 请求中按如下方式设置参数：

```java
"android_root_params": {"pw_channel": "push"} // here you need to specify the name for your channel with custom sound, for example, "push" for the notifications with push.wav sound.
"android_sound": "push" // here you should specify the file name without extension
```

一旦您发送了带有这些指定参数的推送，就会为所有 Android 8+ 设备创建带有选定声音的通知渠道。

现在，要发送带有自定义声音的推送，您只需指定与该声音关联的渠道即可。

### 自定义通知声音的 Proguard 规则

如果您的应用使用 proguard 进行代码和资源压缩，保持您的声音文件完整并可供外部库使用非常重要。如果您在 **build.gradle** 中使用 **`minifyEnabled = true`** 属性，请将以下规则添加到您的 **proguard-rules.pro** 中：

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

如果您在代码压缩的基础上通过使用 **`shrinkResources=true`** 属性来压缩应用的资源，您应额外指定要保留的资源。为此，请创建一个任意名称的新 XML 文件，将其保存在您项目的某个位置（例如，在 res/xml 中），并在 **`resources`** 标签下的 **`tools:keep`** 参数中指定资源名称：

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

## Android SDK meta-data 标志完整列表

要设置一个标志，您需要将 meta-data 块添加到 **AndroidManifest.xml** 文件中的 **application** 标签内。例如，如果您想设置 Pushwoosh 应用程序 ID，请将以下代码添加到您的 **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">设置 Pushwoosh 应用程序 ID。</td><td align="center">XXXXX-XXXXX</td></tr><tr><td align="center">com.pushwoosh.log_level</td><td align="center">设置日志记录级别。有关详细信息，请参阅 <a href="#controlling-log-level">控制日志级别</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">覆盖 Pushwoosh 服务器基础 URL。</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="#customising-notification-open-behaviour">自定义通知打开行为</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="#customizing-push-notifications">自定义推送通知</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">限制 <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，则前台服务会与 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">设置当为“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">设置当为“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>

## 通过 TTL (Time-To-Live) 删除推送通知

要使用 TTL (Time-to-Live) 在指定时间段后自动删除推送通知，请按照以下步骤操作：

1.  创建一个自定义 NotificationFactory。[了解更多](#customizing-push-notifications)

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) // time in milliseconds before the notification is canceled
                                           .build();
    }
}

```

## 与我们分享您的反馈

您的反馈有助于我们创造更好的体验，因此如果您在 SDK 集成过程中遇到任何问题，我们很乐意听取您的意见。如果您遇到任何困难，请随时通过[此表格](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform)与我们分享您的想法。