FCM离线消息推送厂商应用开通指南(个推)

FCM离线消息推送厂商应用开通指南(个推)

FCM

1.简介

消息推送支持集成 Firebase 云信息传递(Firebase Cloud Messaging,简称 FCM)通道,以满足 App 在海外安卓设备上的使用推送的需求,该服务由 Google 拥有的 Firebase 公司提供。若app需要上架海外应用市场,建议您使用 Google Play专版 客户端 SDK 。

2.创建FCM应用

进入 Firebase官网 创建项目,获取 google-services.json 文件及 Server key 。

1,登录 google 账号,如果没有账号请先注册

2,登录后点击右上角的“Go to console”

3,打开项目列表页面,点击 “Add project” 创建项目

4,输入项目名称(根据自己应用取名),点击 “Continue”

5,确认是否需要使用 Google Analytics 服务(根据自己需要开启或关闭),点击 “Continue”

6,确认后创建项目,点击 “Continue” 

进入项目详情页面,点击 “Android” 图标添加 Android 应用

​ 

7,输入 Android 应用信息(包名、昵称、证书SHA-1),点击 “Register App”

8,注册 Android 应用后下载配置文件 “google-services.json”,保存 google-services.json 文件后面需要使用

点击 “Next” 继续

9,此步骤中的操作云端打包机已经处理,忽略提示信息,继续点击“Next”,进入下一步

完成注册 Android 应用,点击 “Continue to console” 回到项目详情页面

10,点击 “Project settings” ,进入项目设置页面

11,切换到 “Cloud Messaging” 项,获取 “Server key”

3.应用配置

  • 在个推开发者中心中填写相关厂商信息:

小米开发平台应用读写文件API标准建议

小米开发平台应用读写文件API标准建议

1.背景与介绍

目前Android 11上Google推出的fuse文件系统和sdcardfs相结合的文件系统。目前常用目录除了storage/emulated/0/Android/data使用的是sdcardfs其他都使用的是fuse文件系统。由于fuse文件系统的特性,会从file的访问到媒体库相关逻辑,所以引发相关性能问题。

2.读写文件API建议

目录建议:

  • 对于私有文件建议存储在storage/emulated/0/Android/data目录下。该目录下不存在权限等判断。
  • 对于共享文件建议存储在storage/emulated/0/Android/media下面。

3.文件的访问

3.1.可以使用直接文件路径和原生库访问文件

为了帮助您的应用更顺畅地使用第三方媒体库,Android 11 允许您使用除 MediaStore API 之外的 API 访问共享存储空间中的媒体文件。不过,您也可以转而选择使用以下任一 API 直接访问媒体文件:

  • File API
  • 原生库,例如 fopen()

如果您的应用没有任何存储权限,您可以使用直接文件路径访问归因于您的应用的媒体文件。如果您的应用具有 READ_EXTERNAL_STORAGE 权限,则可以使用直接文件路径访问所有媒体文件,无论这些文件是否归因于您的应用。如果您直接访问媒体文件,建议您在应用的清单文件中将 requestLegacyExternalStorage 设置为 true 以停用分区存储。这样,您的应用就可以在搭载 Android 10 的设备上正常工作。

3.2.对于性能的影响

当您使用直接文件路径依序读取媒体文件时,其性能与 MediaStore API 相当。但是,当您使用直接文件路径随机读取和写入媒体文件时,进程的速度可能最多会慢一倍。在此类情况下,我们建议您改为使用 MediaStore API。根据目前情况来看,会慢很多,建议使用MediaStore API。

3.3.媒体库中的可用值

当您访问现有媒体文件时,您可以使用您的逻辑中 DATA 列的值。这是因为,此值包含有效的文件路径。但是,不要假设文件始终可用。请准备好处理可能发生的任何基于文件的 I/O 错误。另一方面,如需创建或更新媒体文件,请勿使用 DATA 列的值。请改用 DISPLAY_NAME 和 RELATIVE_PATH 列的值。详见:https://developer.android.google.cn/preview/privacy/storage#change-details

4.MediaStore的使用

由于R版本上使用File写文件目前存在性能问题,因此google推荐使用MediaStore进行写文件。而MediaStore写文件又会存在fuse同步的问题,一旦出现此类同步问题,产生文件无法打开,文件长度不对等异常现象。具体主要一下几点:

  • MediaStore写文件,在文件未真正close的情况,不要调用fd的close操作。

   以下为录音的部分代码,使用MediaStore方式进行音频录制,该fd传递给MediaRecord后,在录制真正完成前该fd不能被close()。正确的示例代码:

ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "rw");
MediaRecorder mr = new MediaRecorder();
mr.setOutputFile(pfd.getFileDescriptor());
......

private void stopRecord() {
    if (mr != null) {
        mr.stop();
        mr.release();
        mr = null;
        if (pfd != null) {
            try{
                pfd.close();
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}
  • MediaStore写文件和File不能混合使用,MediaStore写文件时,不能使用File获取文件大小、重命名等操作。同时,在fd close后,由于回调有延迟,如果紧接着调用file的rename操作,会导致本次rename执行的文件异常。因此需要fd close后,需要延迟1-2s进行file的相关操作。

详见googleAPI文档:https://developer.android.google.cn/training/data-storage/shared/media

小米开发平台Activity Embedding 适配指南

小米开发平台Activity Embedding 适配指南

1、功能介绍

为了利用大屏幕的显示区域,Google在Jetpack WindowManager中添加了Activity Embedding,实现同一应用内不同activity分屏显示,开发者可以通过配置XML文件或者进行Jectpack WindowManager API调用确定如何显示activity(并排或堆叠)。

目前支持以下两种逻辑跳转,第一种是可以实现左容器界面固定:

第二种是支持多层次导航:

另外系统会自动维护对小屏幕的显示,当应用在小屏幕的设备上时,activity会相互堆叠,在大屏幕上,activity会并排显示,对于折叠屏,会随着设备折叠和展开而堆叠和并排显示activity。

2、快速上手

JetPack WindowManager库添加了ActivityEmbeddingComponent,可以根据分屏规则创建容器实现应用内分屏,配置分屏规则涉及到下面几个步骤:

2.1 添加依赖项

将WindowManager库依赖项添加到应用的build.gradle文件中

implementation("androidx.window:window:1.1.0-alpha02")

2.2 配置分屏规则

2.2.1 XML静态配置

放在res资源文件的xml文件夹下

    <!-- The split configuration for activities. -->
    
    <resources
      xmlns:window="http://schemas.android.com/apk/res-auto">
      <!-- Automatically split the following activity pairs. -->
      <SplitPairRule
        window:clearTop="true"
        window:splitMinWidth="600dp">
        <SplitPairFilter
          window:primaryActivityName=".SplitActivityList"
          window:secondaryActivityName=".*"/>
      </SplitPairRule>
    
      <!-- Automatically launch a placeholder for the list activity. -->
    
      <SplitPlaceholderRule
        window:placeholderActivityName=".SplitActivityListPlaceholder"
        window:splitRatio="0.3"
        window:splitMinWidth="600dp">
        <ActivityFilter
          window:activityName=".SplitActivityList"/>
      </SplitPlaceholderRule>    
      
    <!-- Automatically launch a full activity. -->
    
    <ActivityRule window:alwaysExpand="true">
        <ActivityFilter 
            window:activityName=".FullActivity"/>
    </ActivityRule>
    
    </resources>

2.2.2 代码动态配置

运行时定义分屏的配置,开发者可以在startActivity或者onCreate时使用

splitController.registerRule(new SplitPairRule(newFilters));

splitController.unRegisterRule(new SplitPairRule(newFilters));

来动态添加/移除规则

protected void onCreate(@Nullable Bundle savedInstanceState) {
    Set<SplitPairFilter> pairFilters = new HashSet<>();
    SplitPairFilter filter = new SplitPairFilter(primaryActivityComponetName, 
            secondaryActivityComponetName, 
            null);
    pairFilters.add(filter);
    SplitPairRule pairRule = new SplitPairRule(pairFilters, 
            SplitRule.FINISH_ADJACENT, 
            SplitRule.FINISH_ALWAYS, 
            true, 
            600, 
            600, 
            0.3f, 
            LayoutDirection.LOCALE);
    SplitController splitController = SplitController.getInstance();
    splitController.registerRule(pairRule);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

2.3 将规则定义通知库

使用Jetpack Startup库在加载应用的其他组件和启动 activity 之前执行初始化。如需启用启动功能,请在应用的 build 文件中添加库依赖项:

implementation("androidx.startup:startup-runtime:1.1.0")

并在manifest中添加:

<!-- AndroidManifest.xml -->
<provider android:name="androidx.startup.InitializationProvider"
  android:authorities="${applicationId}.androidx-startup"
  android:exported="false"
  tools:node="merge">
  <!-- This entry makes ExampleWindowInitializer discoverable. -->
  <meta-data android:name="**androidx.window.sample.embedding.ExampleWindowInitializer**"
    android:value="androidx.startup" />
</provider>

2.4 添加初始化程序类实现

通过将包含定义 (main_split_config) 的 xml 资源文件的 ID 提供给 SplitController.initialize() 来设置规则:

class ExampleWindowInitializer extends Initializer<SplitController> {

  @Override
  SplitController create(Context context) {
    SplitController.initialize(context, R.xml.main_split_config);
    return SplitController.getInstance(context);
  }

  @Override
  List<Class<? extends Initializer<?>>> dependencies() {
    return emptyList();
  }

}

2.5 XML配置文件参数解析

参数含义
SplitPairRule分屏配对情景(两侧容器均有具体activity),对应SplitPairFilter
splitRatio分屏比默认为0.5f,即左右5:5分屏 ,对于IM类应用,可考虑设置分屏比为0.3f,即左右3:7分屏
splitMinWidth默认配置600dp,宽度达到600dp才可以分屏,主窗口可分屏显示的最小窗口宽度
splitMinSmallestWidth默认配置600dp,主窗口可分屏显示的最小sw值
finishPrimaryWithSecondary默认为  false,true:若secondary container中所有activity都finish,则primary container中创建分屏的activity也会finish,不推荐应用主动配置此项。(androidx.window:window:1.1.0-alpha03中默认是SplitRule.FINISH_ADJACENT)
finishSecondaryWithPrimary默认为 ture, true:若primary container中所有activity都finish,则secondary container中所有activity也会finish,不推荐应用主动配置此项。(androidx.window:window:1.1.0-alpha03中默认是SplitRule.FINISH_ALWAYS)
clearTop默认为 false,true:启动activity窗口分屏,存在相同的primary container,若新建secondary container,则原secondary container中的activity会被finish掉,推荐应用配置,避免右分屏出现多实例
SplitPairFilter分屏配对关系(必需配置)
primaryActivityName分屏的primay activity component name
secondaryActivityName分屏的secondary activity component name
secondaryActivityAction分屏的secondary activity 启动的action (配置此项需要在启动的时候添加action) 
参数含义
SplitPlaceholderRule用来描述分屏下的占位,对应ActivityFilter(必需配置)
placeholderActivityIntentName通过同时打开两个activity创建分屏,secondary占位activity component name
componentName组件名
intentActionintent
splitRatio分屏比默认为0.5f,即左右5:5分屏
splitMinWidth默认配置600dp,宽度达到600dp才可以分屏主窗口可分屏显示的最小窗口宽度
splitMinSmallestWidth默认配置600dp,主窗口可分屏显示的最小sw值
ActivityFilter适配占位规则的activity(必需配置)
参数含义
ActivityRule需要全屏显示的activity,对应ActivityFilter(必需配置
alwaysExpand取值”true”或false true:启动的activity全屏显示
componentName组件名
intentActionintent
ActivityFilter适配全屏规则的activity(必需配置)

2.6 应用主动适配生效

2.6.1 MIUI meta-data标记

AndroidManifest.xml中声明下列meta-data字段。设置为true表示app已经主动适配,系统不会反向适配

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
    <meta-data android:name="embedded" android:value="true"/>
</application>

注意:

Android 13后该meta-data将不使用

2.6.2 Google property标记

Google官方添加property字段,用来标识APP是否允许系统反向适配Activity Embedding

true表示允许系统反向适配,false不允许系统反向适配

注意:自适配Activity Embedding和不允许系统反向适配Activity Embedding的APP需要设置为false

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
    <property 
        android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE" 
        android:value="false" />
</applicatio

3、适配指导

3.1 关于应用横竖屏

3.1.1 PAD 竖屏下进入ActivityEmbedding

平板设备处于竖屏状态时,我们推荐应用采用左下图的全屏显示样式,而不是进入ActivityEmbedding。竖屏下的左右分屏显示会导致信息量过多,难以聚焦。如果您的应用在竖屏下进入了ActivityEmbedding,原因是:应用设置的进入ActivityEmbedding的阈值splitMinWidth过小

  • 获取屏幕大小

R版本之前:Display.getRealSize()、Display.getRealMetrics()。

R版本之后:WindowManager.getMaximumWindowMetrics() 。Jetpack WindowManager中支持使用WindowMetrics maximumWindowMetrics = WindowMetricsCalculator.

getOrCreate().computeMaximumWindowMetrics(activity);

  • 获取当前窗口大小

R版本之前: Display.getSize() 。

R版本之后:WindowManager.getCurrentWindowMetrics() 。Jetpack WindowManager中支持使用WindowMetrics maximumWindowMetrics = WindowMetricsCalculator.

getOrCreate(). computeCurrentWindowMetrics(activity);

推荐解决方案:开发者根据不同屏幕大小的设备动态计算进入分屏的阈值。可使用如下计算方式:

// 获取屏幕大小
WindowMetrics currentWindowMetrics = WindowMetricsCalculator.getOrCreate().
        computeCurrentWindowMetrics(activity);
// 获取当前窗口大小
WindowMetrics maximumWindowMetrics = WindowMetricsCalculator.getOrCreate().
        computeMaximumWindowMetrics(activity);
// 通过configuration获取desity
float density = (float) activity.getResources().getConfiguration().densityDpi / 160f;
// 根据屏幕大小动态设置window:splitMinWidth="600dp"大小,控制竖屏下不进ActivtiyEmbedding
// 为了保证折叠屏外屏下不进入ActivityEmbedding,需要设置一下splitMinSmallestWidth的值,默认600dp
int splitMinWidth = (int) (Math.min(maximumWindowMetrics.getBounds().width(), 
        maximumWindowMetrics.getBounds().height()) / density + 1);

3.1.2 PAD无法横屏

原因:ActivityEmbedding 只是改变activity的显示位置,不会强制更改方向,需要app支持横屏

推荐方案:第一个启动的activity方向设置为behind以及全屏启动的activity设置为behind

3.1.3 折叠屏控制在外屏竖屏显示

为了避免您适配ActivityEmbedding后在折叠屏外屏出现如下图所示的情况,需要控制应用在折叠屏外屏竖屏显示,监听 WindowInfoTracker 来手动设置方向,具体使用参考官方文档https://developer.android.com/guide/topics/large-screens/make-apps-fold-aware

3.2 支持resizeable

应用需要在 AndroidManifest.xml 文件的 application 或者 actvivity 标签中添加 resizeableActivity=true 的属性。

若配置android:resizeableActivity=“false”,会导致无法分屏显示

<application
    android:resizeableActivity="true">
    <activity
          android:resizeableActivity="true" />
</application>

3.3 关于Configuration

当应用在折叠屏内外屏或平板横竖屏交替使用时,activity的大小会发生变化,我们强烈推荐Activity在大小切换时不重启来保证良好的连续性体验,遵循google规范,在android:configChanges 属性增加 screenSize|screenLayout|orientation|smallestScreenSize,并在Activity的onConfigurationChanged回调中更新宽高刷新各个子布局。

3.4 视频全屏

分屏时请求横屏还是在分屏下显示,无法横屏全屏

建议配置activity 始终填满任务窗口:

<ActivityRule  
    window:alwaysExpand="true">
    <ActivityFilter    
        window:activityName=".FullActivity"/>
</ActivityRule>

3.5 clearTop标记

若配置为true

启动activity窗口分屏,存在相同的primary container,会创建新的secondary container,原来的secondary container中的activity会被finish掉,避免右分屏出现多实例。开发者可以根据需要对单独的SplitPairRule 进行配置clearTop=”true”。

用户折叠手机时,屏幕 B 在屏幕 A 之上,屏幕 A 又在菜单之上。当用户从屏幕 B 进行返回导航时,系统会显示屏幕 A 而不是Menu。

可以将分屏配置为通过 clearTop 清除之前的辅助容器,并正常启动新的 activity。

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

3.6 new Task与Launch Flag

当分屏任务窗口中的 activity 启动新任务中的 activity 时,新任务将与包含分屏的任务分开并显示在全窗口中。

在分屏任务窗口中,Launch flag同样起作用,可以复用activity。

3.7 多进程规则匹配

问题:同一Task任务下,子进程配置activity成全屏不生效

采用Jetpack Startup不指定android:process时默认在主进程初始化SplitController,在xml文件夹中的分屏规则不会在子进程生效

<!-- AndroidManifest.xml -->
<provider android:name="androidx.startup.InitializationProvider"
  android:authorities="${applicationId}.androidx-startup"
  android:exported="false"
  tools:node="merge">
  <!-- This entry makes ExampleWindowInitializer discoverable. -->
  <meta-data android:name="**androidx.window.sample.embedding.ExampleWindowInitializer**"
    android:value="androidx.startup" />
</provider>

目前解决方案是需要重写Application在onCreate方法中对需要规则匹配的进程初始化SplitController并且加载静态规则。

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        SplitController.initialize(getApplicationContext(), R.xml.split_config);
        super.onCreate();
    }
}

3.8 响应分屏状态变化

ActivityEmbedding下如何判断activity是否处于分屏以及window mode变化,如何实时响应分屏状态变化。

3.8.1 判断是否在左右分屏

左右分屏下,activity中的configuration的mode是multi-window(与系统分屏下的mode一样,在进行部分业务处理时需要注意)

全屏状态下,activity中的configuration的mode是fullscreen

判断当前activity是否在左右分屏状态,可以通过SplitController.getInstance().isActivityEmbedded(Activity activity)进行区分,返回true表示处于分屏。

3.8.2 如何响应分屏状态变化

为了知道 activity 何时在分屏中,可以向SplitController注册一个监听器来监听分屏状态的变化。然后,相应地调整界面:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    splitController
        .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback());
}

class SplitInfoChangeCallback extends Consumer<List<SplitInfo>> {
    public void accept(List<SplitInfo> splitInfoList) {
        findViewById<View>(R.id.infoButton).visibility =
            !splitInfoList.isEmpty()) ? View.GONE : View.VISIBLE;
    }
}

可以在任何生命周期状态下进行回调,包括当 activity 停止时。通常应在onStart()中注册监听器,在onStop()中取消注册监听器。

3.9 占位Activity Placeholder

如需创建带有占位符的分屏,请创建一个占位符并将其与主要 activity 相关联:

<SplitPlaceholderRule  
window:placeholderIntentName=".Placeholder">  
<ActivityFilter    
    window:activityName=".Main"/>
</SplitPlaceholderRule>

注意:开发者不需要自己启动placeholder,会创建多个TaskFragment导致启动异常,在真正显示主页面时建议不启动placeholder,可以动态注册规则,比如在进入主界面前先跳转到登录界面,如果启动了placeholder有可能会导致异常。

小米开放平台相机各场景下3A操作适配说明

小米开放平台相机各场景下3A操作适配说明

1.Preview

在预览时AF可以由App控制触发对焦,也可以底层自动对焦,推荐使用底层自动对焦。

1.1.App手动对焦设置

  • CONTROL_AF_MODE_AUTO并不是AF自动模式,根据Google定义,是由App进行手动触发。App控制触发对焦逻辑是设置AF为Auto,然后App主动触发对焦。
  • App使用CONTROL_AF_TRIGGER触发对焦。CONTROL_AF_TRIGGER使用建议,CANCEL – START – IDLE;App何时触发对焦由App决定,比方App检测到加速度计和陀螺仪变化达到一定的阈值或者app为扫码软件,检测扫码一直无法解析扫码结果等(因为出现这种场景时物距发生了变化或者焦点模糊),具体场景具体调整。对于扫码等APP还可以设置AF测量区域,CONTROL_AF_REGIONS,尽量使用中心区域作为对焦点。

1.2.AF自动对焦设置

可以先检测AF有效模式,如果支持CONTINUOUS_PICTURE,设置即可,具体如下:

mCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES),CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE))。

CONTROL_AF_MODE设置为CONTROL_AF_MODE_CONTINUOUS_PICTURE。

2.Video

  • 可以先检测AF有效模式,如果支持CONTINUOUS_VIDEO,设置即可,具体如下:
mCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES),CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO))。

CONTROL_AF_MODE设置为CONTROL_AF_MODE_CONTINUOUS_VIDEO。

  • 注释

CONTROL_AF_MODE_CONTINUOUS_PICTURE和CONTROL_AF_MODE_CONTINUOUS_VIDEO的区别是前者在AF时会快速收敛,后者在AF时慢速收敛,因为录制视频时为了防止图像抖动,收敛过程需要慢一点,共同点是二者都为自动对焦。

3.Touch

Touch是为了在预览时对特定的区域进行3A权重提高,以便于得到预期的图像。当touch预览窗口某区域时,通用的操作流程是设置CONTROL_AF_REGIONS,CONTROL_AF_TRIGGER的流程还是CANCEL – START – IDLE。

4.Flash

Flash的实用场景通常在Touch,拍照和录像情况下使用,flash有两种操作模式,手动模式和自动模式,具体操作如下:

4.1.手动模式,即Torch模式

当需要打开flash时,设置CONTROL_AF_MODE为OFF/AUTO模式,同时设置FLASH_MODE为torch即可。

4.2.自动模式

自动模式是flash交给底层AE算法控制,具体如下两种常用模式:

ON_AUTO_FLASH:设置AF_Mode为此模式表示flash由AE根据当前亮度进行打开,关闭。

ON_ALWAYS_FLASH:设置AF_Mode为此模式表示flash每次操作都会打开。

5.拍照

拍照操作流程通常为触发3A,等待3A收敛,获取到3A收敛结果,进行拍照请求。

5.1.触发3A操作

CONTROL_AE_MODE根据是否需要闪光灯设置为ON_AUTO_FLASH,ON_ALWAYS_FLASH,ON。

CONTROL_AE_PRECAPTURE_TRIGGER设置建议 CANCEL-START-IDLE。

CONTROL_AF_MODE设置为AUTO,CONTINUOUS_VIDEO都可以。

CONTROL_AF_TRIGGER设置建议CANCEL-START-IDLE。

5.2.等待3A状态收敛

根据CaptureResult的metadata获取3A状态,如果3A都收敛了即可请求拍照

result.get(CaptureResult.CONTROL_AF_STATE);   //AF_State

result.get(CaptureResult.CONTROL_AE_STATE);   //AE_State

result.get(CaptureResult.CONTROL_AWB_STATE); //AEB_State 

5.3进行拍照请求

CONTROL_AE_MODE根据是否需要闪光灯设置为ON_AUTO_FLASH,ON_ALWAYS_FLASH,ON。

CONTROL_AF_MODE设置为AUTO,CONTINUOUS_VIDEO都可以。

最终下发拍照请求:

mCaptureSession.capture(request, mCaptureCallback, mBackgroundHandler); 

6.参考

https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics
https://developer.android.com/reference/android/hardware/camera2/CaptureRequest
https://github.com/googlearchive/android-Camera2Raw

谷歌或有计划向部分APP开放谷歌应用商店google pay第三方支付权限

大家都知道无论谷歌或者苹果,都在旗下的APP应用商店采取了唯一的支付通道,apple的是IOS内购,google pay也是谷歌支付,APP一旦涉及收费,都需要向苹果或谷歌公司分成。

图:google pay

这个传统已经延续了很多年了,也导致很多开发者,特别是涉及虚拟收费的服务或者游戏充值的公司非常的苦闷,因为应用商店抽成实在是有点多,比如苹果apple商店appstore抽成高达30%。google pay根据不同的业务抽成比例不一,但是也是很高的。

这种状态可能在不久的将来或许会有改变,近日谷歌就有意向的放出了一些信息,表达出未来一段时间可能会在某一些地区或国家,允许某一些特定的APP使用它们自己喜欢的支付方式。

这意味着未来APP将不用强制性的通过google pay的统一支付渠道,可能无需再向google分成。

图:google pay

这也许不是公司层面的本意,但是很可能是受到外界的强烈干预的结果,因为前段时间荷兰对苹果开出了连续9周高达500万美金的垄断罚款。未来可能鉴于地区或国家行政层面的强大压力,APP应用商店的限制性支付渠道和分成可能会有好转。

Material Design 是 Google 打造的、具有超强表现力和适应性的设计系统

(图:Material Design)

Material Design 是 Google 打造的、具有超强表现力和适应性的设计系统,包含设计准则、组件和工具,助力实现用户界面设计的最佳实践。Material Design 是开源开放的,提供了一个可自定义的大型组件库,能够满足各种样式和品牌需求,从而可以帮助您的团队在 Android、Flutter 和 Web 领域创造高质量的数字体验。

在 2021 年的 I/O 大会上,我们展示了 Material Design 大胆而富有表现力的演变。Material Design 3 在 Material Design Primary 颜色和 Secondary 颜色的基础上,引入了 Tertiary 颜色和附加色槽,用于验证无障碍访问功能并保障显示的和谐性。在本篇文章中,我们将为您展示更多有关 Material You 动态配色的内容,包括动态配色是什么,以及如何在您的应用中实现它。


如果您更喜欢通过视频了解此内容,请在此处查看:以下视频来源于Android 开发者,时长10:36

△ 借助 Material You 动态配色丰富您的应用

  • Bilibili 视频链接
    https://www.bilibili.com/video/BV1qS4y127Px/

动态配色

Material You 通过动态配色,将颜色重新定义为更加个性化的体验。那么,什么是动态配色?Android 12 可以通过动态配色提取算法来选择颜色值,基于动态配色,您可根据用户的桌面壁纸颜色生成自定义调色板。动态的浅、深色方案可体现在整个用户系统界面,以及某些应用中。这些方案将根据用户偏好和视觉需求进行更改或调整。动态配色是一种算法系统,支持个性化颜色体验的同时还尊重蕴含品牌标识或具有传统意义的颜色,如绿色代表 “Go”,红色代表 “Stop”。

△ 动态配色可提取壁纸主色调动态配色的原理

首先,它从用户的壁纸上提取一种源颜色,并推算出五种关键颜色,然后将每个关键颜色转化为由 13 种色调组成的调色板。接下来,它会为从调色板中选择的颜色分配一组特定的角色和值,并将这些角色和值映射到我们称之为 “方案” 的组件上。

△ 从壁纸中提取关键颜色

△ 从关键颜色生成调色板

配色方案

配色方案可视为一组拼合在一起的相关色调,而非一组固定不变的值。每种色调都会生成一组浅、深色方案,这些方案将根据偏好和视觉需求进行更改或调整。Error 颜色也会自动分配到相应色槽中。界面需要的每个颜色角色,都会经历这个过程: 通过元素之间的适当对比,从关键颜色派生出来。这些颜色角色就是您在设计中要映射到的内容。

△ 不同颜色的浅深色方案
您可能已经熟悉了目前的 12 个颜色槽,比如其中的 Primary 色调和 On Primary 色调。在 Material Design 3 (或简称为 M3) 中,我们在设计方案里引入了新调色板和角色,您可将 Container 颜色及其对应的 On Container 颜色,用于无需像非容器元素那样强调的界面元素。另外,新的 Tertiary 颜色调色板,则用于为您的产品带来更广泛的色彩表现力。

△ 新增的 Container 及 Tertiary 颜色色槽

Token
使用动态配色,意味着我们需要使用会在运行时改变的值构建界面,这就需要我们根据语义,而非硬编码的值来引用颜色。那么如何使这一切成为可能呢?方法是使用 Token。Token 在 Material Design 2 颜色角色的基础上,提供了全局的样式色槽,它能够帮您更改与颜色级联一致的角色分配。

△ 在界面中使用 Token

Token 可以有多种类型,它可以与某个值配对或引用另一个 Token。在使用了 M3 后,我们就有了调色板、色彩引用和系统 Token 三个概念。您创建的颜色角色是系统 Token,它们可以继承我们在调色板中引用的 Token,包括 Primary、Secondary、Tertiary、Neutral、Neutral Variant 以及 Error 颜色。

设计 Token 使得整个产品更具灵活性和一致性,它允许设计师们为界面中元素指定颜色角色,而非设定一个用于实现的值。生成设计 Token 时可通过确定一个单一事实来源,来节省开发者和设计师的时间。例如,开发者可以引用设计 Token 文件以映射到 Compose 中的主题对象;而如果您在代码中更改了 Token,则可以与设计师共享这些更改,以便设计师在其设计中更新这些值。

△ 使用 Token 为界面元素指定颜色角色
通过使用 Token 替代颜色和排版的硬编码值,您可以更轻松地对设计进行迭代。色调调色板中的颜色可通过设计 Token 映射到浅、深色彩方案中,同时颜色方案的值也可以被重写,以便继承自定义颜色或其他色彩引用的 Token。

△ 色值、调色板及系统颜色 Token 间的映射关系

您可以利用这些带有 Token 的颜色映射,将用户生成的颜色转变为动态且富有表现力的界面。当系统颜色在运行过程中发生变化时便会更新调色板以及配色方案,而后者便是您映射到主题背景和组件的配色方案。在相应的组件上使用正确的颜色规则,以确保可以无障碍访问和风格的连续性,这是至关重要的一点。

△ 相同的 Token,不同的色值

无障碍访问但是这些 Token 本身如何确保色彩的无障碍访问?由于配色方案是由调色定义的,而非色调或十六进制值,所以,为了使任何配色方案在默认情况下均可满足无障碍访问,颜色组合要基于亮度来满足无障碍使用的标准。

△ 颜色组合需要基于亮度来满足无障碍访问

如下图所示,在亮度接近时,尽管两种颜色的色调并不相同,但调色却非常相似。这样的色彩组合使得对比度过低,而对于有一定程度色盲的人来说更是如此。如果对组件应用这样的色彩组合,则会导致无法满足无障碍访问。因此,为了保证颜色在无障碍层面的可及性,所有成对颜色均存在 60 的亮度差。

△ 色调、亮度对于对比度的影响

自定义扩展

动态配色让个人设备变得更为个性化。一旦您在产品界面中加入个性化设置,用户将比以往任何时候都能更好地控制他们的设备。通过使用动态配色和 M3 配色方案,用户壁纸将能够影响应用的配色方案,您的应用颜色会自动适应与集成用户的壁纸颜色。

这对于您应用的配色来讲,也许是一种全新思维方式。但我们如今所创建的数字化产品,会反映出现实生活中的产品趋势,这意味着更多个性化的色彩、图案和元素。在我们需要一套配色时,如果您觉得自己的调色板不合适或缺少可用资源时,动态配色可为您提供用户喜欢的、现成的、可无障碍访问的调色板。我们充分理解,您可能需要品牌配色方案成为用户瞩目的焦点,所以最新的配色系统可以在支持无障碍访问的同时融入应用的颜色背景。您可以使用自己的品牌和设计系统颜色创造出和谐的、可无障碍访问的调色板。M3 支持自定义参数的系统化应用,这有助于您定义和维护品牌。在 Android 应用上,自定义配色方案也可以作为禁用动态配色后的备用方案。不过,无论是使用动态配色,还是自定义配色,要将您的 Android 应用迁移并使用 Material 3,您首先需要迁移到基础颜色或 M3 自定义方案来访问新的 Token。

△ M3 基础颜色角色

我们始终鼓励您利用 Material Design 并根据需要进行扩展。M3 的颜色系统可以与自定义组件和品牌风格相结合,通过自动处理关键调整,满足无障碍访问的颜色对比度,保障易读性、交互状态和组件结构。

  • Material Design
    https://m3.material.io/

迁移至 Material 3

接下来向您介绍如何将应用迁移至 Material 3。迁移的第一步是引用新 Token 并将其与应用中的组件连接。Material 3 的排版、形状和颜色文件与 Material 2 十分类似,请您确保获取到最新基础颜色或自定义品牌方案并设置值。
Material Theme Builder

M3 中有一些新的 Token 需要注意,例如 Primary、Secondary、Tertiary Container 以及 On Variants 系列颜色。对您来说,管理这些颜色可能非常费力,因此我们创建了一个名为 Material Theme Builder 的工具来为您生成这些内容。您可在网页中打开它并点击 “Custom”,然后点击 “Export for Compose”。

  • Material Theme Builder
    https://material-foundation.github.io/material-theme-builder/

如您有自定义颜色,可将其添加为扩展颜色。我们可以打开该工具并切换到 “Custom” 标签页,在 Material Theme Builder 中您可以识别并输入一种或多种品牌颜色,这些颜色将用于定义调色板,通过添加特定颜色可确定每个调色板的生成方式。如下图所示,您可在左侧输入品牌的关键颜色,每种颜色都会分配到相应的关键颜色角色,具体情况视其在界面中的用途而定。如果您有现成的应用,您可以使用 Material 2 中的颜色配置 Primary 和 Secondary 颜色。随后,您可以点击右上角的导出代码菜单,然后在下拉列表中选择 “Compose”。

△ 自定义颜色方案

最后,您可将这些文件直接放入 Android Studio,并在必要时更新软件包。这一操作将更新颜色、排版和主题背景文件,更新代码后您即可运行应用来查看组件映射的新品牌主题背景。

使用动态配色您可使用上述的网页工具,预览基于源颜色或图像生成的各种方案。接下来,我们将讨论如何基于用户所选图像所生成的颜色更新应用。请您打开 Kotlin 文件 theme,并添加检查来查看您是否有使用动态配色,然后您可以根据条件返回由系统调色板创建的深浅方案颜色。


val dynamic = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colorScheme = if (dynamic) {
       val context = LocalContext.current
       if (dark) dynamicLightColorScheme (context) else dynamicDarkColorScheme (context)
} else {
       // 使用 lightColorScheme、darkColorScheme 等
}

添加上述代码后,即可在设备上运行应用并更改壁纸,此时将显示用户生成的用于主题背景的颜色。您可添加一个切换开关,以便用户在动态或自定义的主题背景之间进行切换,这两种方案都提供了深色和浅色两种方案供用户选择。您可以有选择地应用动态配色,并与品牌配色方案同时生效。例如,个人资料或帐户界面可展示个人的用户颜色,使重要的时刻变得个性化;如果您有语义颜色,则不必将其排除在外,而是可以将其包含在准备实现的其他颜色之内。这意味着您可为应用的主要主题、语义上的扩展颜色,甚至品牌颜色使用动态配色;或者您使用自己丰富的颜色库。

△ 配色方案随用户设置的壁纸变化

结语

凭借动态、品牌化的主题,Material You 能够体现用户对于颜色的选择,帮助您构建出色且富有表现力的应用;同时设计 Token 也有助于开发者和设计师的协作。我们十分期待看到您构建的应用!有关动态配色的更多信息,请使用 Material Theme Builder 或安装 Figma 插件,该插件可与更新的 M3 设计工具包配合使用。您可通过浏览 Figma 社区获得该插件,从而实现动态配色的可视化,并创建自定义配色方案。

  • Material Theme Builder
    https://material-foundation.github.io/material-theme-builder/
  • Figma 插件
    https://www.figma.com/community/plugin/1034969338659738588

(本文转载至谷歌开发者,您可以通过扫码以下二维码关注谷歌开发者,更多谷歌专业精彩文章请移步谷歌开发者

(图:谷歌开发者)

巨头影视版权之争?苹果apple TV禁止谷歌安卓用户购买apple TV内容

近日国外苹果公司宣布旗下apple TV全面禁止在谷歌平台上进行内容分发,具体表现为apple Tv内容从谷歌TV和安卓TV上全面下架,也就是说Android TV 和 Google TV的用户将无法再在本平台便捷的购买apple TV上的节目和内容。

(图:apple Tv)

apple tv一直是国际上非常重量级的音视频影视音乐版权分发渠道,他基本上掌握着全球非常多核心的文化娱乐版权,能够与之抗衡的也就是谷歌,以前apple TV的内容可以在Android TV 和 Google TV上直接进行购买,苹果和谷歌两家公司也有着内部的分佣分发分成协议。这一次的合作破裂,在外界看来可以代表着两家公司开始了内容版权交火。

(图:Android TV)

当然也可能只是暂时的停止合作,也许只是分佣比列两个大佬没有谈好。

这一骚操作可以说苦逼了国外的观众,以前通过Android TV 和 Google TV就可以快捷购买到apple Tv上的内容,现在只能更换设备硬件,在苹果设备上,直接进入apple Tv才可以购买内容观看。

(图:Google TV)

如果没有苹果设备,还没办法观看之前的内容视频了,这无疑加大了普通消费者的使用成本。可以说巨头之间的策略,最后苦逼了的还是普通人。当然也有人表示这是苹果的深谋远虑,及时的构筑好自己的内容生态护城河。

为何这么说呢?

这几年其实可以看到安卓手机厂商的硬件,已经和苹果硬件的技术水平相差无几,再过几年,可能苹果硬件的技术优势会被逐渐的柔化或者消失。除开硬件方面的创新不足,产品疲惫,软件方面谷歌也是拼着老命不停的优化系统。可以看到这几年的谷歌安卓系统,完全是快马加鞭,一马当前,玩着命的更新,系统级的更新从安卓8910到11,到12,现在安卓13,软件方面的体验已经和苹果IOS系统差别不大。

可以想象当硬件优势和软件优势都不明显的情况下,普通消费者的选择性就不会那么的明确和坚定,再在其他厂家设备价格优势下,可能会流失很多用户。

如果到那时候苹果就很恼火了,那么为了应对可能出现的极端情况,提前几年部署一下生态防火墙,加筑几道版权护城河是非常有必要的。

话说回来,这什么apple TV、Google TV、Android TV在咋们内地都是不容许的,未取得前置许可证的,所以与内地消费者无关,大家一起坐着吃瓜!