分类目录归档:Android

Ubuntu 14.04 AOSP源码编译指南

1.先安装编译环境需要的软件

sudo apt-get install openjdk-7-jdk git-core gnupg flex bison gperf build-essential \

zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \

lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \

libgl1-mesa-dev libxml2-utils xsltproc unzip

2.同步代码

#新建目录
mkdir workspace
cd workspace
#链接为清华镜像,--repo-url是repo工具的地址,--no-repo-verify关闭repo工具验证
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-6.0.1_r79 --repo-url=https://mirrors.tuna.tsinghua.edu.cn/git/git-repo --no-repo-verify
#-d用清单文件里的版本;-c当前分支;--no-tags不拉取tag
repo sync -d -c --no-tags

3.开始编译

#初始化环境
source build/envsetup.sh
#选择产品
lunch
#用16线程编译
make -j16

关于Android CPU架构产生的应用运行异常问题提示

Android开发中,不可避免的可能会引用到外部so文件,设置一个项目中可能需要引用多个不同的外部so文件。因为不同的引入库中so文件的目录可能不同,导致打包后生成的项目lib目录中的目录结构是不同的外部so文件目录的合集。可能会出现armeabi/armeabi-v7a/arm64-v8a/x86/mips等,一般情况下,armeabi应该是有的,当此三个目录下的文件可能不同时,在某些特定机型下很可能会出现UnsatisfiedLinkError

原因在于不同的机型CPU结构不同导致搜寻不同的目录下面的包,而由于外部库不同的so文件目录可能armeabi下还有a、b so文件,而x86下可能只含有a。例如一个应用armeabi下有liba.solibb.so,arm64-v8a下有liba.so这样的应用装在arm64的设备上,当代码掉到加载libb.so时就会有上面的异常出现,即便是armeabi下有此so,系统也不会去寻找,原因是这个apk中已经有arm64的目录了。

此时解决方案如下:

在build.gradle中添加如下内容

    android {
        defaultConfig {
            ndk {
                abiFilters 'armeabi'
            }
        }
    }

这样生成的apk中就只包含armeabi下的so文件,而不包含其他架构的目录,这样系统会采取兼容模式,加载armeabi下的so文件。

pk8和x509.pem转换成keystore

一、
在github上下载工具
https://github.com/getfatday/keytool-importkeypair
将工具在Linux环境下解压或者解压后Copy到Linux下,运行如下命令

keytool-importkeypair -k android/debug.keystore -p android -pk8 android/platform.pk8 -cert android/platform.x509.pem -alias androiddebugkey

二、
1 把pk8转换成pk12格式

openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out platform.priv.pem -nocrypt

2 生成pk12的密钥问文件

openssl pkcs12 -export -in platform.x509.pem -inkey platform.priv.pem -out platform.pk12 -name androiddebugkey

3 生成keystore

keytool -importkeystore -deststorepass android -destkeypass android -destkeystore debug.keystore -srckeystore platform.pk12 -srcstoretype PKCS12 -srcstorepass android -alias androiddebugkey

Android交叉编译curl

操作系统:ubuntu 14.04
一.安装需要工具

sudo apt-get install autoconf libtool libsysfs-dev

二.制作交叉编译工具链
1.下载NDK,并配置NDK环境变量为NDK的安装路径
2.根据NDK里docs文档里的standalone-toolchain.html来抽取交叉编译的环境。
3.配置SYSROOT环境变量:

SYSROOT=$NDK/platforms/android-19/arch-arm //android-19是你的android开发版本所定

4.然后运行命令:

$NDK/build/tools/make-standalone-toolchain.sh --platform=android-19 --install-dir=/tmp/my-android-toolchain

/tmp/my-android-toolchain是你交叉编译环境的复制路径。最好别放在tmp目录里,因为重启机子就立即消失了。这个新生成的文件 夹即是你的交叉编译环境
5.配置PAHT和CC环境变量:

export PATH=/tmp/my-android-toolchain/bin:$PATH
export CC=arm-linux-androideabi-gcc

三.下载curl并编译

git clone https://github.com/curl/curl.git
cd curl
mkdir build
autoreconf -i
./configure --host=arm-linux --enable-cross-compile --enable-threaded-resolver --disable-shared --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smtp --disable-gopher --disable-manual --enable-proxy --enable-ipv6 --enable-cookies --enable-symbol-hiding --disable-versioned-symbols --disable-soname-bump --disable-sspi --disable-ntlm-wb --prefix=`pwd`/build
make 
make install

GRADLE多渠道打包

示例脚本

apply plugin: 'com.android.application'

def releaseTime() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

android {
    compileSdkVersion 21
    buildToolsVersion '21.1.2'

    defaultConfig {
        applicationId "com.boohee.*"
        minSdkVersion 14
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"

        // dex突破65535的限制
        multiDexEnabled true
        // 默认是umeng的渠道
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
    }

    lintOptions {
        abortOnError false
    }

    signingConfigs {
        debug {
            // No debug config
        }

        release {
            storeFile file("../yourapp.keystore")
            storePassword "your password"
            keyAlias "your alias"
            keyPassword "your password"
        }
    }

    buildTypes {
        debug {
            // 显示Log
            buildConfigField "boolean", "LOG_DEBUG", "true"

            versionNameSuffix "-debug"
            minifyEnabled false
            zipAlignEnabled false
            shrinkResources false
            signingConfig signingConfigs.debug
        }

        release {
            // 不显示Log
            buildConfigField "boolean", "LOG_DEBUG", "false"

            minifyEnabled true
            zipAlignEnabled true
            // 移除无用的resource文件
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release

            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        // 输出apk名称为boohee_v1.0_2015-01-15_wandoujia.apk
                        def fileName = "boohee_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
        }
    }

    // 友盟多渠道打包
    productFlavors {
        wandoujia {}
        _360 {}
        baidu {}
        xiaomi {}
        tencent {}
        taobao {}
        ...
    }

    productFlavors.all { flavor ->
        flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:support-v4:21.0.3'
    compile 'com.jakewharton:butterknife:6.0.0'
    ...
}

如何用gradle生成javadoc

在模块的build.gradle中填入以下内容

task javadoc(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}

javadoc {
    options {
        encoding "UTF-8"
        charSet 'UTF-8'
        author true
        version true
        splitIndex true
        noDeprecated true
        setMemberLevel(JavadocMemberLevel.PUBLIC)
        links "http://docs.oracle.com/javase/8/docs/api"
    }
}

下面还有一个可能更好使

android.libraryVariants.all { variant ->
    task("generate${variant.name}Javadoc", type: Javadoc) {
        title = "$name $version API"
        source = variant.javaCompile.source
        ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
        classpath = files(variant.javaCompile.classpath.files, ext.androidJar)
        options {
            links("http://docs.oracle.com/javase/8/docs/api/");
            linksOffline("http://d.android.com/reference", "${android.sdkDirectory}/docs/reference");
            encoding "UTF-8"
            charSet 'UTF-8'
            author true
            version true
            splitIndex true
            noDeprecated true
            setMemberLevel(JavadocMemberLevel.PUBLIC)
        }
        failOnError false
        exclude '**/BuildConfig.java'
        exclude '**/R.java'
    }
}

怎样搭建本地AOSP Gerrit服务器

怎样搭建本地AOSP Gerrit服务器

在这个教程里,我会介绍怎样搭建一个本地Android源码Gerrit服务器。

完成这个教程后,你会拥有一个完全可运行的AOSP镜像和本地Gerrit服务器。

首先我们需要一个Linux服务器。这里用Ubuntu 14.04

Gerrit 需求:

1.Java JDK > 1.7

2.Git

3.SSH server

4.DB

我选择mysql作为数据库服务,当然你也可以用其他的数据库软件。可以参考Gerrit文档

这里有一句命令可以让你得到你所需要的一切。

$ sudo apt-get install git openjdk-8-jdk openssh-server mysql-server gitweb

下载并安装Android repo

$ sudo curl https://storage.googleapis.com/git-repo-downloads/repo > /usr/bin/repo
$ sudo chmod a+x /usr/bin

现在我们需要设置一个本地Android镜像,这需要一段时间,你可以先去喝杯咖啡。

$ mkdir -p /usr/local/aosp/mirror
$ cd /usr/local/aosp/mirror
$ repo init -u https://android.googlesource.com/mirror/manifest --mirror
$ repo sync

下载Gerrit

创建一个Gerrit用户

$ sudo adduser --system --shell /bin/bash --gecos 'Gerrit Code Review User' --group --disabled-password --home /home/gerrit2 gerrit2

Gerrit需要一个数据库来工作,这里用的是mysql。打开终端输入以下命令:

$ mysql -u root -p
CREATE USER 'gerrit2'@'localhost' IDENTIFIED BY 'secret';
CREATE DATABASE reviewdb;
ALTER DATABASE reviewdb charset=latin1;
GRANT ALL ON reviewdb.* TO 'gerrit2'@'localhost';
FLUSH PRIVILEGES;
quit

上面这个命令在mysql中创建了一个gerrit用户,一个数据库和gerrit用户需要的权限集合。

用新的系统用户登陆:

$ sudo su gerrit2

复制上面下载的文件gerrit-2.13.5.war到gerrit2的home下,然后执行下面的命令:

$ java -jar ./gerrit-2.11.war init -d review_site
Using secure store: com.google.gerrit.server.securestore.DefaultSecureStore

*** Gerrit Code Review 2.11
***

Create ‘/home/gerrit2/review_site’ [Y/n]?

*** Git Repositories
***

Location of Git repositories [git]:

*** SQL Database
***

Database server type [h2]: MySQL

Gerrit Code Review is not shipped with MySQL Connector/J 5.1.21
** This library is required for your configuration. **
Download and install it now [Y/n]?
Downloading http://repo2.maven.org/maven2/mysql/mysql-connector-java/5.1.21/mysql-connector-java-5.1.21.jar … OK
Checksum mysql-connector-java-5.1.21.jar OK
Server hostname [localhost]:
Server port [(mysql default)]:
Database name [reviewdb]:
Database username [gerrit2]: gerrit2
gerrit’s password :
confirm password :

*** Index
***

Type [LUCENE/?]:

*** User Authentication
***

Authentication method [OPENID/?]:

*** Review Labels
***

Install Verified label [y/N]?

*** Email Delivery
***

SMTP server hostname [localhost]:
SMTP server port [(default)]:
SMTP encryption [NONE/?]:
SMTP username :

*** Container Process
***

Run as [gerrit2]:
Java runtime [/usr/lib/jvm/java-8-openjdk-amd64/jre]:
Copy gerrit-2.11.war to /home/gerrit2/review_site/bin/gerrit.war [Y/n]?
Copying gerrit-2.11.war to /home/gerrit2/review_site/bin/gerrit.war
*** SSH Daemon
***

Listen on address [*]:
Listen on port [29418]:

Gerrit Code Review is not shipped with Bouncy Castle Crypto SSL v151
If available, Gerrit can take advantage of features
in the library, but will also function without it.
Download and install it now [Y/n]?
Downloading http://www.bouncycastle.org/download/bcpkix-jdk15on-151.jar … OK
Checksum bcpkix-jdk15on-151.jar OK
Generating SSH host key … rsa… dsa… done

*** HTTP Daemon
***

Behind reverse proxy [y/N]?
Use SSL (https://) [y/N]?
Listen on address [*]:
Listen on port [8080]:
Canonical URL [http://ubuntu:8080/]: http://10.0.0.9:8080

*** Plugins
***

Installing plugins.
Install plugin download-commands version v2.11 [y/N]?
Install plugin reviewnotes version v2.11 [y/N]?
Install plugin singleusergroup version v2.11 [y/N]?
Install plugin replication version v2.11 [y/N]?
Install plugin commit-message-length-validator version v2.11 [y/N]?
Initializing plugins.
No plugins found with init steps.

Initialized /home/gerrit2/review_site
Executing /home/gerrit2/review_site/bin/gerrit.sh start
Starting Gerrit Code Review: OK
Waiting for server on ubuntu:8080 … OK
Opening http://10.0.0.9:8080/#/admin/projects/ …FAILED
Open Gerrit with a JavaScript capable browser:
http://10.0.0.9:8080/#/admin/projects/

现在Gerrit已经开始运行了。

需要注意的是,第一个登陆到gerrit的用户将会成为管理员。

Gerrit能通过多种认证方式登陆。上面选择的OpenID。

现在让我们打开浏览器并打开Gerrit网站。

注册并登陆后需要设置SSH keys,这样我们就能够在命令行下工作。

下面我们在Ubuntu系统的另一个用户下执行以下命令:

$ ssh-keygen <ENTER>
Generating public/private rsa key pair.
Enter file in which to save the key (/home/serveradmin/.ssh/id_rsa):  <ENTER>
Enter passphrase (empty for no passphrase): <ENTER>
Enter same passphrase again: <ENTER>
Your identification has been saved in /home/serveradmin/.ssh/id_rsa.
Your public key has been saved in /home/serveradmin/.ssh/id_rsa.pub.
The key fingerprint is:
d5:7b:51:d8:22:0e:95:63:f9:0e:a2:22:1c:97:76:40 serveradmin@ubuntu
The key's randomart image is:
+---[RSA 2048]----+
| .E ..o o.|
| . ..* o..|
| o .+.+.. |
| . + ... o... |
| . + .S. ..o. |
| o . . .. |
| . . |
| |
| |
+-----------------+

$ cat ~/.ssh/id_rsa.pub  <ENTER>
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDobCUbb8ExP9ci48ZCzCUE3P+IjoH6zrv/l88+4NOTf9FJWDAl4SHCXI+mrZoFEeZo9
uieKTuvHqUFQNpnVA9vfNY6bhaTucAAt0fX9Q1M1ZtNj2IxaQd7u9PnxjSGEig0BUtjqDEu4CMhMShXGWsGAwL7ju/qu7G7RF
iK/Wtcye6wUSjziXseCusb1DUZZ6dsOpxrPYEM3kwXpItQtAq1oEIQWsxEgj2nrOqRXm1UWdqIQU1X45XHQtg5iqi44PfLVNU
3alu453MeWn5PrpSS5dFw/7AkBW4KMPwrOvVnu8gb9xLA0TWtPf+sQ9ROEQC5SBeO+4Q9XRNf5YaswpLj 

在网站的右上角点击你的用户名然后点击Settings,在左侧的菜单中选择“SSH Public Keys”,复制上面的内容到里面,然后点Add。

下面让我们测试一下通过SSH访问Gerrit:

ssh -p 29418 admin@localhost <ENTER> <Change "admin" to your username in gerrit>

**** Welcome to Gerrit Code Review ****

Hi Administrator, you have successfully connected over SSH.

Unfortunately, interactive shells are disabled.
To clone a hosted Git repository, use:

git clone ssh://ramon@10.0.0.9:29418/REPOSITORY_NAME.git
Connection to localhost closed.

出现以上信息说明登陆成功,下面创建两个group:“android-admin” and “android”

前者用来管理,包含review,merge,delete等权限,后者只有view和change-set的权限。

在网页中点击People -> Create New Group,输入名称“android-admin”。执行同样的操作创建“android”。

现在我们为AOSP代码树创建一个父项目。所有为这个项目的设置都会继承到子项目。如果跳过这步,AOSP项目会继承“All-Projects”项目,而这个项目太通用了。

在网站中点击Project -> Create New Project,填入以下内容:

Project Name: Android
Rights Inherit From: All-Projects

勾选 “Only Serve As Parent For Other Projects”,然后点击“Create Project”按钮。

项目已经创建完,下面来为这个项目设置权限控制:

点击Projects -> List,然后选择“Android”项目,点击“Access”然后点击“Edit”,修改成如下图。图中还包含了其他的群组,可自行设置。

Gerrit权限配置

现在我们可以把所有的Android项目推送到Gerrit了。

第一条命令会在Gerrit上创建所有的Android项目;第二条命令会将刚刚创建的项目的父项目修改为“Android”;第三条命令会将代码推送到Gerrit,时间会比较长。

进入下载Android镜像的目录执行以下命令:

$ repo forall -c 'echo $REPO_PATH; ssh -p 29418 admin@localhost gerrit create-project --name android/$REPO_PATH --owner android;'

accessories/manifest
device/asus/deb
device/asus/flo
device/asus/flo-kernel
device/asus/fugu
device/asus/fugu-kernel
…

$ repo forall -c 'echo $REPO_PATH; ssh -p 29418 admin@localhost gerrit set-project-parent --parent Android android/$REPO_PATH;'

accessories/manifest
device/asus/deb
device/asus/flo
device/asus/flo-kernel
…

$ repo forall -c 'echo $REPO_PATH; git push ssh://ramon@localhost:29418/android/$REPO_PATH +refs/heads/* +refs/tags/*;'

大功告成!

创建Gerrit服务自启:

$ sudo echo "GERRIT_SITE=/opt/gerrit-review" >> /etc/default/gerritcodereview
$ sudo ln -snf /opt/gerrit-review/bin/gerrit.sh /etc/init.d/gerrit
$ sudo update-rc.d gerrit defaults

下面贴一个gerrit配置文件(postgre数据库,ldap认证,提交信息增加jira链接)

[gerrit]
    basePath = git
    canonicalWebUrl = http://localhost:8080/
[database]
    type = postgresql
    hostname = localhost
    database = reviewdb
    username = gerrit2
[index]
    type = LUCENE
[auth]
    type = LDAP
[ldap]
    server = ldap://localhost:389
    username = cn=admin,dc=darkerthanblack,dc=org
    accountBase = ou=People,dc=darkerthanblack,dc=org
    groupBase = ou=group,dc=darkerthanblack,dc=org
[receive]
    enableSignedPush = false
[sendemail]
    smtpServer = smtp.exmail.qq.com
    smtpServerPort = 465
    smtpEncryption = SSL
    smtpUser = example@example.com
        smtpPass = your_password
        from = example@example.com
[container]
    user = gerrit2
    javaHome = /usr/lib/jvm/java-8-openjdk-amd64/jre
[sshd]
    listenAddress = *:29418
    maxConnectionsPerUser = 0
[httpd]
    listenUrl = http://*:8080/
[cache]
    directory = cache
[gitweb]
    cgi = /usr/lib/cgi-bin/gitweb.cgi
[commentlink "jira"]
    match = ([A-Z]+-[0-9]+)
    link = http://localhost:8081/browse/$1

参考:
http://openwares.net/linux/gerrit2_setup.html
https://nativeguru.wordpress.com/2015/08/18/how-to-set-local-aosp-gerrit-server-part-1

Android 4.4媒体库路径问题

Android 4.4媒体库路径问题

  1. public static String getPath(final Context context, final Uri uri) {
  2.  
  3.     final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
  4.  
  5.     // DocumentProvider
  6.     if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
  7.         // ExternalStorageProvider
  8.         if (isExternalStorageDocument(uri)) {
  9.             final String docId = DocumentsContract.getDocumentId(uri);
  10.             final String[] split = docId.split(":");
  11.             final String type = split[0];
  12.  
  13.             if ("primary".equalsIgnoreCase(type)) {
  14.                 return Environment.getExternalStorageDirectory() + "/" + split[1];
  15.             }
  16.  
  17.             // TODO handle non-primary volumes
  18.         }
  19.         // DownloadsProvider
  20.         else if (isDownloadsDocument(uri)) {
  21.  
  22.             final String id = DocumentsContract.getDocumentId(uri);
  23.             final Uri contentUri = ContentUris.withAppendedId(
  24.                     Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
  25.  
  26.             return getDataColumn(context, contentUri, null, null);
  27.         }
  28.         // MediaProvider
  29.         else if (isMediaDocument(uri)) {
  30.             final String docId = DocumentsContract.getDocumentId(uri);
  31.             final String[] split = docId.split(":");
  32.             final String type = split[0];
  33.  
  34.             Uri contentUri = null;
  35.             if ("image".equals(type)) {
  36.                 contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
  37.             } else if ("video".equals(type)) {
  38.                 contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
  39.             } else if ("audio".equals(type)) {
  40.                 contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
  41.             }
  42.  
  43.             final String selection = "_id=?";
  44.             final String[] selectionArgs = new String[] {
  45.                     split[1]
  46.             };
  47.  
  48.             return getDataColumn(context, contentUri, selection, selectionArgs);
  49.         }
  50.     }
  51.     // MediaStore (and general)
  52.     else if ("content".equalsIgnoreCase(uri.getScheme())) {
  53.  
  54.         // Return the remote address
  55.         if (isGooglePhotosUri(uri))
  56.             return uri.getLastPathSegment();
  57.  
  58.         return getDataColumn(context, uri, null, null);
  59.     }
  60.     // File
  61.     else if ("file".equalsIgnoreCase(uri.getScheme())) {
  62.         return uri.getPath();
  63.     }
  64.  
  65.     return null;
  66. }
  67.  
  68. /**
  69.  * Get the value of the data column for this Uri. This is useful for
  70.  * MediaStore Uris, and other file-based ContentProviders.
  71.  *
  72.  * @param context The context.
  73.  * @param uri The Uri to query.
  74.  * @param selection (Optional) Filter used in the query.
  75.  * @param selectionArgs (Optional) Selection arguments used in the query.
  76.  * @return The value of the _data column, which is typically a file path.
  77.  */
  78. public static String getDataColumn(Context context, Uri uri, String selection,
  79.         String[] selectionArgs) {
  80.  
  81.     Cursor cursor = null;
  82.     final String column = "_data";
  83.     final String[] projection = {
  84.             column
  85.     };
  86.  
  87.     try {
  88.         cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
  89.                 null);
  90.         if (cursor != null && cursor.moveToFirst()) {
  91.             final int index = cursor.getColumnIndexOrThrow(column);
  92.             return cursor.getString(index);
  93.         }
  94.     } finally {
  95.         if (cursor != null)
  96.             cursor.close();
  97.     }
  98.     return null;
  99. }
  100.  
  101.  
  102. /**
  103.  * @param uri The Uri to check.
  104.  * @return Whether the Uri authority is ExternalStorageProvider.
  105.  */
  106. public static boolean isExternalStorageDocument(Uri uri) {
  107.     return "com.android.externalstorage.documents".equals(uri.getAuthority());
  108. }
  109.  
  110. /**
  111.  * @param uri The Uri to check.
  112.  * @return Whether the Uri authority is DownloadsProvider.
  113.  */
  114. public static boolean isDownloadsDocument(Uri uri) {
  115.     return "com.android.providers.downloads.documents".equals(uri.getAuthority());
  116. }
  117.  
  118. /**
  119.  * @param uri The Uri to check.
  120.  * @return Whether the Uri authority is MediaProvider.
  121.  */
  122. public static boolean isMediaDocument(Uri uri) {
  123.     return "com.android.providers.media.documents".equals(uri.getAuthority());
  124. }
  125.  
  126. /**
  127.  * @param uri The Uri to check.
  128.  * @return Whether the Uri authority is Google Photos.
  129.  */
  130. public static boolean isGooglePhotosUri(Uri uri) {
  131.     return "com.google.android.apps.photos.content".equals(uri.getAuthority());
  132. }