Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63b1553f authored by Jonathan Klee's avatar Jonathan Klee
Browse files

Merge remote-tracking branch 'upstream/master'

parents 1969b58f 541c61ff
Loading
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
<img src="http://i.imgur.com/hXY4lcC.png" height="42px" alt="microG" /> Services Core (GmsCore)
microG Services
=======
[![Build Status](https://github.com/microg/GmsCore/workflows/Build/badge.svg)](https://travis-ci.com/microg/GmsCore)
[![Build status](https://github.com/microg/GmsCore/actions/workflows/build.yml/badge.svg)](https://github.com/microg/GmsCore/actions/workflows/build.yml)

microG GmsCore is a FLOSS (Free/Libre Open Source Software) framework to allow applications designed for Google Play Services to run on systems, where Play Services is not available.
microG Services is a FLOSS (Free/Libre Open Source Software) framework to allow applications designed for Google Play Services to run on systems, where Play Services is not available.

### Please refer to the [wiki](https://github.com/microg/android_packages_apps_GmsCore/wiki) for downloads and instructions
### Please refer to the [wiki](https://github.com/microg/GmsCore/wiki) for downloads and instructions


License
-------
    Copyright 2013-2022 microG Project Team
    Copyright 2013-2023 microG Project Team

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
+8 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
 */

apply plugin: 'com.android.library'
apply plugin: 'com.squareup.wire'
apply plugin: 'kotlin-android'

dependencies {
@@ -11,6 +12,9 @@ dependencies {
    implementation project(':play-services-base-core')

    implementation "androidx.appcompat:appcompat:$appcompatVersion"

    implementation "com.android.volley:volley:$volleyVersion"
    implementation "com.squareup.wire:wire-runtime:$wireVersion"
}

android {
@@ -38,3 +42,7 @@ android {
        jvmTarget = 1.8
    }
}

wire {
    kotlin {}
}
+6 −7
Original line number Diff line number Diff line
@@ -15,14 +15,13 @@
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

        <activity
            android:process=":ui"
            android:name="org.microg.gms.appinivite.AppInviteActivity"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
        <activity-alias
            android:name="com.google.android.gms.appinvite.AppInviteAcceptInvitationActivity"
            android:targetActivity="org.microg.gms.appinivite.AppInviteActivity"
            android:exported="true">
            android:excludeFromRecents="true"
            android:process=":ui"
            android:exported="true"
            android:theme="@style/Theme.AppCompat.Light.Dialog.NoActionBar">
            <intent-filter
                android:priority="900"
                android:autoVerify="true">
@@ -46,6 +45,6 @@
                <data android:scheme="https" />
                <data android:scheme="http" />
            </intent-filter>
        </activity-alias>
        </activity>
    </application>
</manifest>
+167 −7
Original line number Diff line number Diff line
@@ -5,21 +5,181 @@

package org.microg.gms.appinivite

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.LocaleList
import android.util.Log
import android.view.ViewGroup
import android.view.Window
import android.widget.ProgressBar
import android.widget.RelativeLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.pm.PackageInfoCompat
import androidx.core.os.bundleOf
import androidx.core.view.setPadding
import androidx.lifecycle.lifecycleScope
import com.android.volley.*
import com.android.volley.toolbox.Volley
import com.squareup.wire.Message
import com.squareup.wire.ProtoAdapter
import kotlinx.coroutines.CompletableDeferred
import okio.ByteString.Companion.decodeHex
import org.microg.gms.appinvite.*
import org.microg.gms.common.Constants
import java.util.*

private const val TAG = "AppInviteActivity"

class AppInviteActivity : Activity() {
private const val APPINVITE_DEEP_LINK = "com.google.android.gms.appinvite.DEEP_LINK"
private const val APPINVITE_INVITATION_ID = "com.google.android.gms.appinvite.INVITATION_ID"
private const val APPINVITE_OPENED_FROM_PLAY_STORE = "com.google.android.gms.appinvite.OPENED_FROM_PLAY_STORE"
private const val APPINVITE_REFERRAL_BUNDLE = "com.google.android.gms.appinvite.REFERRAL_BUNDLE"

class AppInviteActivity : AppCompatActivity() {
    private val queue by lazy { Volley.newRequestQueue(this) }

    private val Int.px: Int get() = (this * resources.displayMetrics.density).toInt()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val uri = intent?.data
        if (uri == null) {
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(ProgressBar(this).apply {
            layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            setPadding(20.px)
            isIndeterminate = true
        })
        val extras = intent.extras
        extras?.keySet()
        Log.d(TAG, "Intent: $intent $extras")
        if (intent?.data == null) return finish()
        lifecycleScope.launchWhenStarted { run() }
    }

    private fun redirectToBrowser() {
        try {
            startActivity(Intent(Intent.ACTION_VIEW).apply {
                addCategory(Intent.CATEGORY_DEFAULT)
                data = intent.data
            })
        } catch (e: Exception) {
            Log.w(TAG, e)
        }
        finish()
            return
    }
        Log.d(TAG, "uri: $uri")
        // TODO datamixer-pa.googleapis.com/

    private fun open(appInviteLink: MutateAppInviteLinkResponse) {
        val intent = Intent(Intent.ACTION_VIEW).apply {
            addCategory(Intent.CATEGORY_DEFAULT)
            data = appInviteLink.data_?.intentData?.let { Uri.parse(it) }
            `package` = appInviteLink.data_?.packageName
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
            putExtra(
                APPINVITE_REFERRAL_BUNDLE, bundleOf(
                    APPINVITE_DEEP_LINK to appInviteLink,
                    APPINVITE_INVITATION_ID to "",
                    APPINVITE_OPENED_FROM_PLAY_STORE to false
                )
            )
        }
        val fallbackIntent = Intent(Intent.ACTION_VIEW).apply {
            addCategory(Intent.CATEGORY_DEFAULT)
            data = appInviteLink.data_?.fallbackUrl?.let { Uri.parse(it) }
        }
        val installedVersionCode = runCatching {
            intent.resolveActivity(packageManager)?.let {
                PackageInfoCompat.getLongVersionCode(packageManager.getPackageInfo(it.packageName, 0))
            }
        }.getOrNull()
        if (installedVersionCode != null && (appInviteLink.data_?.app?.minAppVersion == null || installedVersionCode >= appInviteLink.data_.app.minAppVersion)) {
            startActivity(intent)
            finish()
        } else {
            try {
                startActivity(fallbackIntent)
            } catch (e: Exception) {
                Log.w(TAG, e)
            }
            finish()
        }
    }

    private suspend fun run() {
        val request = ProtobufPostRequest(
            "https://datamixer-pa.googleapis.com/v1/mutateonekey?alt=proto&key=AIzaSyAP-gfH3qvi6vgHZbSYwQ_XHqV_mXHhzIk", MutateOperation(
                id = MutateOperationId.AppInviteLink,
                mutateRequest = MutateDataRequest(
                    appInviteLink = MutateAppInviteLinkRequest(
                        client = ClientIdInfo(
                            packageName = Constants.GMS_PACKAGE_NAME,
                            signature = Constants.GMS_PACKAGE_SIGNATURE_SHA1.decodeHex().base64(),
                            language = Locale.getDefault().language
                        ),
                        link = LinkInfo(
                            empty = "",
                            uri = intent.data.toString()
                        ),
                        system = SystemInfo(
                            gms = SystemInfo.GmsInfo(
                                versionCode = Constants.GMS_VERSION_CODE
                            )
                        )
                    )
                )
            ), MutateDataResponseWithError.ADAPTER
        )
        val response = try {
            request.sendAndAwait(queue)
        } catch (e: Exception) {
            Log.w(TAG, e)
            return redirectToBrowser()
        }
        if (response.errorStatus != null || response.dataResponse?.appInviteLink == null) return redirectToBrowser()
        open(response.dataResponse.appInviteLink)
    }
}

class ProtobufPostRequest<I : Message<I, *>, O>(url: String, private val i: I, private val oAdapter: ProtoAdapter<O>) :
    Request<O>(Method.POST, url, null) {
    private val deferred = CompletableDeferred<O>()

    override fun getHeaders(): Map<String, String> {
        val headers = HashMap(super.getHeaders())
        headers["Accept-Language"] = if (Build.VERSION.SDK_INT >= 24) LocaleList.getDefault().toLanguageTags() else Locale.getDefault().language
        headers["X-Android-Package"] = Constants.GMS_PACKAGE_NAME
        headers["X-Android-Cert"] = Constants.GMS_PACKAGE_SIGNATURE_SHA1
        return headers
    }

    override fun getBody(): ByteArray = i.encode()

    override fun getBodyContentType(): String = "application/x-protobuf"

    override fun parseNetworkResponse(response: NetworkResponse): Response<O> {
        try {
            return Response.success(oAdapter.decode(response.data), null)
        } catch (e: VolleyError) {
            return Response.error(e)
        } catch (e: Exception) {
            return Response.error(VolleyError())
        }
    }

    override fun deliverResponse(response: O) {
        Log.d(TAG, "Got response: $response")
        deferred.complete(response)
    }

    override fun deliverError(error: VolleyError) {
        deferred.completeExceptionally(error)
    }

    suspend fun await(): O = deferred.await()

    suspend fun sendAndAwait(queue: RequestQueue): O {
        Log.d(TAG, "Sending request: $i")
        queue.add(this)
        return await()
    }
}
 No newline at end of file
+93 −0
Original line number Diff line number Diff line
syntax = "proto2";
option java_package = "org.microg.gms.appinvite";

message ClientIdInfo {
    optional string packageName = 3; // e.g. com.google.android.gms
    optional string signature = 4; // Signing certificate sha-1 base64 with padding, e.g. WOHEEz90Qew9LCcCcKFIAtpHug4=
    optional string language = 6; // e.g. en
}

message LinkInfo {
    optional string empty = 1; // e.g. ""
    optional string uri = 2;
}

message SystemInfo {
    message GmsInfo {
        optional uint32 versionCode = 1; // 212423054
    }
    optional GmsInfo gms = 1;
}

message MutateAppInviteLinkRequest {
    optional ClientIdInfo client = 1;
    optional LinkInfo link = 4;
    optional SystemInfo system = 5;
}

message MutateDataRequest {
    oneof request {
        MutateAppInviteLinkRequest appInviteLink = 84453462;
    }
}

message AppInviteLinkInfo {
    optional int32 type = 1;
    optional string url = 2;
    optional string name = 3;
}

message AppInviteAppData {
    optional string packageName = 1; // apn
    optional uint64 minAppVersion = 2; // amv
    optional string altPackageName = 3; //apn
}

message AppInviteLinkData {
    optional string fallbackUrl = 1; // afl
    optional string packageName = 2; // apn
    optional string intentData = 3; // link
    optional AppInviteAppData app = 6;
}

message AppInviteLinkMetadata {
    optional string source = 2; // utm_source
    optional string medium = 3; // utm_medium
    optional string campaign = 4; // utm_campaign
    optional string id = 5;
    optional string appCode = 6;
    optional AppInviteLinkInfo info = 8;
    optional string sessionId = 9;
    optional string domainUriPrefix = 10;
    optional string content = 11; // utm_content
    optional string term = 12; // utm_term
}

message MutateAppInviteLinkResponse {
    optional AppInviteLinkData data = 1;
    optional AppInviteLinkMetadata metadata = 4;
}

message MutateDataResponse {
    oneof response {
        MutateAppInviteLinkResponse appInviteLink = 84453462;
    }
}

enum MutateOperationId {
    AppInviteLink = 84453462;
}

message MutateOperation {
    optional MutateOperationId id = 1; // 84453462
    optional MutateDataRequest mutateRequest = 2;
}

message StatusProto {
    optional int32 code = 1;
}

message MutateDataResponseWithError {
    optional MutateDataResponse dataResponse = 1;
    optional StatusProto errorStatus = 2;
}
 No newline at end of file
Loading