Repo Created
This commit is contained in:
parent
eb305e2886
commit
a8c22c65db
4784 changed files with 329907 additions and 2 deletions
224
play-services-core/build.gradle
Normal file
224
play-services-core/build.gradle
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2013 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
configurations {
|
||||
mapboxRuntimeOnly
|
||||
vtmRuntimeOnly
|
||||
if (hasModule("hms", true)) hmsRuntimeOnly
|
||||
defaultRuntimeOnly
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.squareup.wire:wire-runtime:$wireVersion"
|
||||
implementation "de.hdodenhof:circleimageview:1.3.0"
|
||||
|
||||
implementation project(':fake-signature')
|
||||
implementation project(':firebase-dynamic-links')
|
||||
implementation project(':firebase-auth-core')
|
||||
implementation project(':play-services-ads-core')
|
||||
implementation project(':play-services-ads-identifier-core')
|
||||
implementation project(':play-services-ads-lite-core')
|
||||
implementation project(':play-services-appinvite-core')
|
||||
implementation project(':play-services-appset-core')
|
||||
implementation project(':play-services-auth-api-phone-core')
|
||||
implementation project(':play-services-auth-blockstore-core')
|
||||
implementation project(':play-services-auth-workaccount-core')
|
||||
implementation project(':play-services-base-core')
|
||||
implementation project(':play-services-cast-core')
|
||||
implementation project(':play-services-cast-framework-core')
|
||||
implementation project(':play-services-conscrypt-provider-core')
|
||||
implementation project(':play-services-cronet-core')
|
||||
implementation project(':play-services-droidguard-core')
|
||||
implementation project(':play-services-fido-core')
|
||||
implementation project(':play-services-fitness-core')
|
||||
implementation project(':play-services-gmscompliance-core')
|
||||
implementation project(':play-services-location-core')
|
||||
implementation project(':play-services-location-core-base')
|
||||
implementation project(':play-services-oss-licenses-core')
|
||||
implementation project(':play-services-panorama-core')
|
||||
implementation project(':play-services-pay-core')
|
||||
implementation project(':play-services-recaptcha-core')
|
||||
implementation project(':play-services-safetynet-core')
|
||||
implementation project(':play-services-tapandpay-core')
|
||||
implementation project(':play-services-threadnetwork-core')
|
||||
implementation project(':play-services-vision-core')
|
||||
implementation project(':play-services-wearable-core')
|
||||
|
||||
implementation project(':play-services-core-proto')
|
||||
|
||||
implementation project(':play-services-core:microg-ui-tools') // deprecated
|
||||
implementation project(':play-services-base-core-package')
|
||||
implementation project(':play-services-api')
|
||||
|
||||
implementation project(':play-services-appinvite')
|
||||
implementation project(':play-services-auth-base')
|
||||
implementation project(':play-services-auth')
|
||||
implementation project(':play-services-clearcut')
|
||||
implementation project(':play-services-drive')
|
||||
implementation project(':play-services-games')
|
||||
implementation project(':play-services-maps')
|
||||
implementation project(':play-services-measurement-base')
|
||||
implementation project(':play-services-places')
|
||||
implementation project(':play-services-recaptcha')
|
||||
implementation project(':play-services-safetynet')
|
||||
implementation project(':play-services-tasks-ktx')
|
||||
implementation project(':play-services-fitness')
|
||||
|
||||
mapboxRuntimeOnly project(':play-services-maps-core-mapbox')
|
||||
vtmRuntimeOnly project(':play-services-maps-core-vtm')
|
||||
defaultRuntimeOnly project(':play-services-location-core-provider')
|
||||
|
||||
if (hasModule("nearby", true)) runtimeOnly project(':play-services-nearby-core-package')
|
||||
if (hasModule("hms", false)) hmsRuntimeOnly project(':play-services-maps-core-hms')
|
||||
|
||||
// AndroidX UI
|
||||
implementation "androidx.multidex:multidex:$multidexVersion"
|
||||
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
||||
implementation "androidx.mediarouter:mediarouter:$mediarouterVersion"
|
||||
implementation "androidx.preference:preference-ktx:$preferenceVersion"
|
||||
implementation "androidx.webkit:webkit:$webkitVersion"
|
||||
|
||||
// Material Components
|
||||
implementation "com.google.android.material:material:$materialVersion"
|
||||
|
||||
// Compose
|
||||
def composeBom = platform('androidx.compose:compose-bom:2024.04.00')
|
||||
implementation composeBom
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||
implementation 'androidx.activity:activity-compose:1.8.2'
|
||||
|
||||
// Navigation
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion"
|
||||
|
||||
implementation "com.android.volley:volley:$volleyVersion"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "com.google.android.gms"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
dataBinding = true
|
||||
compose true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
versionCode appVersionCode
|
||||
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
|
||||
multiDexEnabled true
|
||||
multiDexKeepProguard file('multidex-keep.pro')
|
||||
|
||||
manifestPlaceholders = [appLabel:"@string/gms_app_name"]
|
||||
resValue "string", "package_id", "com.google.android.gms"
|
||||
|
||||
buildConfigField "String", "SAFETYNET_KEY", "\"${localProperties.get("safetynet.key", "")}\""
|
||||
buildConfigField "String", "RECAPTCHA_SITE_KEY", "\"${localProperties.get("recaptcha.siteKey", "")}\""
|
||||
buildConfigField "String", "RECAPTCHA_SECRET", "\"${localProperties.get("recaptcha.secret", "")}\""
|
||||
buildConfigField "String", "RECAPTCHA_ENTERPRISE_PROJECT_ID", "\"${localProperties.get("recaptchaEnterpreise.projectId", "")}\""
|
||||
buildConfigField "String", "RECAPTCHA_ENTERPRISE_SITE_KEY", "\"${localProperties.get("recaptchaEnterpreise.siteKey", "")}\""
|
||||
buildConfigField "String", "RECAPTCHA_ENTERPRISE_API_KEY", "\"${localProperties.get("recaptchaEnterpreise.apiKey", "")}\""
|
||||
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
||||
}
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.10"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'MissingTranslation', 'GetLocales', 'InvalidPackage', 'BatteryLife', 'ImpliedQuantity', 'MissingQuantity', 'InvalidWakeLockTag', 'UniquePermission'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions = ['maps', 'target']
|
||||
productFlavors {
|
||||
"default" {
|
||||
dimension 'target'
|
||||
}
|
||||
"huawei" {
|
||||
dimension 'target'
|
||||
versionNameSuffix "-hw"
|
||||
}
|
||||
"huaweilh" {
|
||||
dimension 'target'
|
||||
versionNameSuffix "-lh"
|
||||
versionCode appVersionCode - 1000
|
||||
matchingFallbacks = ['huawei']
|
||||
}
|
||||
"user" {
|
||||
dimension 'target'
|
||||
applicationId = "org.microg.gms"
|
||||
versionNameSuffix "-user"
|
||||
manifestPlaceholders = [appLabel:"@string/limited_services_app_name"]
|
||||
matchingFallbacks = ['default']
|
||||
resValue "string", "package_id", "org.microg.gms"
|
||||
}
|
||||
"hms" {
|
||||
dimension 'maps'
|
||||
}
|
||||
"mapbox" {
|
||||
dimension 'maps'
|
||||
}
|
||||
"vtm" {
|
||||
dimension 'maps'
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = 1.8
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/ASL2.0'
|
||||
jniLibs {
|
||||
useLegacyPackaging true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file('user.gradle').exists()) {
|
||||
apply from: 'user.gradle'
|
||||
}
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
output.outputFileName = variant.applicationId + "-" + variant.versionCode + variant.versionName.substring(version.length()) + ".apk"
|
||||
}
|
||||
}
|
||||
46
play-services-core/microg-ui-tools/build.gradle
Normal file
46
play-services-core/microg-ui-tools/build.gradle
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2013-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
namespace "org.microg.tools.ui"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
// TODO: Remove MissingTranslation once we have stable strings and proper translations.
|
||||
disable 'MissingTranslation'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
||||
implementation "androidx.preference:preference:$preferenceVersion"
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest />
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.selfcheck;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import org.microg.tools.ui.R;
|
||||
|
||||
import static org.microg.tools.selfcheck.SelfCheckGroup.Result.Negative;
|
||||
import static org.microg.tools.selfcheck.SelfCheckGroup.Result.Positive;
|
||||
|
||||
@RequiresApi(23)
|
||||
public class PermissionCheckGroup implements SelfCheckGroup {
|
||||
private static final String TAG = "SelfCheckPerms";
|
||||
|
||||
private String[] permissions;
|
||||
|
||||
public PermissionCheckGroup(String... permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupName(Context context) {
|
||||
return context.getString(R.string.self_check_cat_permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doChecks(Context context, ResultCollector collector) {
|
||||
for (String permission : permissions) {
|
||||
doPermissionCheck(context, collector, permission);
|
||||
}
|
||||
}
|
||||
|
||||
private void doPermissionCheck(Context context, ResultCollector collector, final String permission) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
PermissionInfo info = pm.getPermissionInfo(permission, 0);
|
||||
CharSequence permLabel = info.loadLabel(pm);
|
||||
collector.addResult(context.getString(R.string.self_check_name_permission, permLabel),
|
||||
context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED ? Positive : Negative,
|
||||
context.getString(R.string.self_check_resolution_permission),
|
||||
fragment -> fragment.requestPermissions(new String[]{permission}, 0));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.selfcheck;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public interface SelfCheckGroup {
|
||||
String getGroupName(Context context);
|
||||
|
||||
void doChecks(Context context, ResultCollector collector);
|
||||
|
||||
interface ResultCollector {
|
||||
void addResult(String name, Result value, String resolution);
|
||||
|
||||
void addResult(String name, Result value, String resolution, CheckResolver resolver);
|
||||
}
|
||||
|
||||
interface CheckResolver {
|
||||
void tryResolve(Fragment fragment);
|
||||
}
|
||||
|
||||
enum Result {
|
||||
Positive, Negative, Unknown
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public abstract class AbstractAboutFragment extends Fragment {
|
||||
|
||||
protected abstract void collectLibraries(List<Library> libraries);
|
||||
|
||||
public static Drawable getIcon(Context context) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
return pm.getPackageInfo(context.getPackageName(), 0).applicationInfo.loadIcon(pm);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Never happens, self package always exists!
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAppName(Context context) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
CharSequence label = pm.getPackageInfo(context.getPackageName(), 0).applicationInfo.loadLabel(pm);
|
||||
if (TextUtils.isEmpty(label)) return context.getPackageName();
|
||||
return label.toString().trim();
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Never happens, self package always exists!
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getAppName() {
|
||||
return getAppName(getContext());
|
||||
}
|
||||
|
||||
public static String getLibVersion(String packageName) {
|
||||
try {
|
||||
String versionName = (String) Class.forName(packageName + ".BuildConfig").getField("VERSION_NAME").get(null);
|
||||
if (TextUtils.isEmpty(versionName)) return "";
|
||||
return versionName.trim();
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSelfVersion(Context context) {
|
||||
return getLibVersion(context.getPackageName());
|
||||
}
|
||||
|
||||
protected String getSelfVersion() {
|
||||
return getSelfVersion(getContext());
|
||||
}
|
||||
|
||||
protected String getSummary() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View aboutRoot = inflater.inflate(R.layout.about_root, container, false);
|
||||
((ImageView) aboutRoot.findViewById(android.R.id.icon)).setImageDrawable(getIcon(getContext()));
|
||||
((TextView) aboutRoot.findViewById(android.R.id.title)).setText(getAppName());
|
||||
((TextView) aboutRoot.findViewById(R.id.about_version)).setText(getString(R.string.about_version_str, getSelfVersion()));
|
||||
String summary = getSummary();
|
||||
if (summary != null) {
|
||||
((TextView) aboutRoot.findViewById(android.R.id.summary)).setText(summary);
|
||||
aboutRoot.findViewById(android.R.id.summary).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
List<Library> libraries = new ArrayList<Library>();
|
||||
collectLibraries(libraries);
|
||||
Collections.sort(libraries);
|
||||
ViewGroup list = aboutRoot.findViewById(android.R.id.list);
|
||||
for (Library library : libraries) {
|
||||
View v = inflater.inflate(android.R.layout.simple_list_item_2, list, false);
|
||||
((TextView) v.findViewById(android.R.id.text1)).setText(getString(R.string.about_name_version_str, library.name, getLibVersion(library.packageName)));
|
||||
((TextView) v.findViewById(android.R.id.text2)).setText(library.copyright != null ? library.copyright : getString(R.string.about_default_license));
|
||||
list.addView(v);
|
||||
}
|
||||
return aboutRoot;
|
||||
}
|
||||
|
||||
protected static class Library implements Comparable<Library> {
|
||||
private final String packageName;
|
||||
private final String name;
|
||||
private final String copyright;
|
||||
|
||||
public Library(String packageName, String name, String copyright) {
|
||||
this.packageName = packageName;
|
||||
this.name = name;
|
||||
this.copyright = copyright;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + ", " + copyright;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Library another) {
|
||||
return name.toLowerCase(Locale.US).compareTo(another.name.toLowerCase(Locale.US));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package org.microg.tools.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractDashboardActivity extends AppCompatActivity {
|
||||
protected int preferencesResource = 0;
|
||||
|
||||
private final List<Condition> conditions = new ArrayList<Condition>();
|
||||
private ViewGroup conditionContainer;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dashboard_activity);
|
||||
conditionContainer = (ViewGroup) findViewById(R.id.condition_container);
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.content_wrapper, getFragment())
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
forceConditionReevaluation();
|
||||
}
|
||||
|
||||
private synchronized void resetConditionViews() {
|
||||
conditionContainer.removeAllViews();
|
||||
for (Condition condition : conditions) {
|
||||
if (condition.isEvaluated()) {
|
||||
if (condition.isActive(this)) {
|
||||
addConditionToView(condition);
|
||||
}
|
||||
} else {
|
||||
evaluateConditionAsync(condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateConditionAsync(final Condition condition) {
|
||||
if (condition.willBeEvaluating()) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (condition.isActive(AbstractDashboardActivity.this)) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (conditions.contains(condition) && condition.isEvaluated()) {
|
||||
addConditionToView(condition);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
protected void forceConditionReevaluation() {
|
||||
for (Condition condition : conditions) {
|
||||
condition.resetEvaluated();
|
||||
}
|
||||
resetConditionViews();
|
||||
}
|
||||
|
||||
protected void addAllConditions(Condition[] conditions) {
|
||||
for (Condition condition : conditions) {
|
||||
addCondition(condition);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addCondition(Condition condition) {
|
||||
conditions.add(condition);
|
||||
if (conditionContainer == null) return;
|
||||
if (condition.isEvaluated()) {
|
||||
addConditionToView(condition);
|
||||
} else {
|
||||
evaluateConditionAsync(condition);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void addConditionToView(Condition condition) {
|
||||
for (int i = 0; i < conditionContainer.getChildCount(); i++) {
|
||||
if (conditionContainer.getChildAt(i).getTag() == condition) return;
|
||||
}
|
||||
conditionContainer.addView(condition.createView(this, conditionContainer));
|
||||
}
|
||||
|
||||
protected void clearConditions() {
|
||||
conditions.clear();
|
||||
resetConditionViews();
|
||||
}
|
||||
|
||||
protected Fragment getFragment() {
|
||||
if (preferencesResource == 0) {
|
||||
throw new IllegalStateException("Neither preferencesResource given, nor overriden getFragment()");
|
||||
}
|
||||
ResourceSettingsFragment fragment = new ResourceSettingsFragment();
|
||||
Bundle b = new Bundle();
|
||||
b.putInt(ResourceSettingsFragment.EXTRA_PREFERENCE_RESOURCE, preferencesResource);
|
||||
fragment.setArguments(b);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.microg.tools.selfcheck.SelfCheckGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static org.microg.tools.selfcheck.SelfCheckGroup.Result.Negative;
|
||||
import static org.microg.tools.selfcheck.SelfCheckGroup.Result.Positive;
|
||||
import static org.microg.tools.selfcheck.SelfCheckGroup.Result.Unknown;
|
||||
|
||||
public abstract class AbstractSelfCheckFragment extends Fragment {
|
||||
private static final String TAG = "SelfCheck";
|
||||
|
||||
private ViewGroup root;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View scrollRoot = inflater.inflate(R.layout.self_check, container, false);
|
||||
root = (ViewGroup) scrollRoot.findViewById(R.id.self_check_root);
|
||||
reset(inflater);
|
||||
return scrollRoot;
|
||||
}
|
||||
|
||||
protected abstract void prepareSelfCheckList(Context context, List<SelfCheckGroup> checks);
|
||||
|
||||
protected void reset(LayoutInflater inflater) {
|
||||
List<SelfCheckGroup> selfCheckGroupList = new ArrayList<SelfCheckGroup>();
|
||||
prepareSelfCheckList(getContext(), selfCheckGroupList);
|
||||
|
||||
root.removeAllViews();
|
||||
for (SelfCheckGroup group : selfCheckGroupList) {
|
||||
View groupView = inflater.inflate(R.layout.self_check_group, root, false);
|
||||
((TextView) groupView.findViewById(android.R.id.title)).setText(group.getGroupName(getContext()));
|
||||
final ViewGroup viewGroup = (ViewGroup) groupView.findViewById(R.id.group_content);
|
||||
final SelfCheckGroup.ResultCollector collector = new GroupResultCollector(viewGroup);
|
||||
try {
|
||||
group.doChecks(getContext(), collector);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Failed during check " + group.getGroupName(getContext()), e);
|
||||
collector.addResult("Self-check failed:", Negative, "An exception occurred during self-check. Please report this issue.");
|
||||
}
|
||||
root.addView(groupView);
|
||||
}
|
||||
}
|
||||
|
||||
private class GroupResultCollector implements SelfCheckGroup.ResultCollector {
|
||||
private final ViewGroup viewGroup;
|
||||
|
||||
public GroupResultCollector(ViewGroup viewGroup) {
|
||||
this.viewGroup = viewGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResult(final String name, final SelfCheckGroup.Result result, final String resolution) {
|
||||
addResult(name, result, resolution, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResult(final String name, final SelfCheckGroup.Result result, final String resolution,
|
||||
final SelfCheckGroup.CheckResolver resolver) {
|
||||
if (result == null || getActivity() == null) return;
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
View resultEntry = LayoutInflater.from(getContext()).inflate(R.layout.self_check_entry, viewGroup, false);
|
||||
((TextView) resultEntry.findViewById(R.id.self_check_name)).setText(name);
|
||||
resultEntry.findViewById(R.id.self_check_result).setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (result == Positive) {
|
||||
((CheckBox) resultEntry.findViewById(R.id.self_check_result)).setChecked(true);
|
||||
resultEntry.findViewById(R.id.self_check_resolution).setVisibility(GONE);
|
||||
} else {
|
||||
((TextView) resultEntry.findViewById(R.id.self_check_resolution)).setText(resolution);
|
||||
if (result == Unknown) {
|
||||
resultEntry.findViewById(R.id.self_check_result).setVisibility(INVISIBLE);
|
||||
}
|
||||
if (resolver != null) {
|
||||
resultEntry.setClickable(true);
|
||||
resultEntry.setOnClickListener(v ->
|
||||
resolver.tryResolve(AbstractSelfCheckFragment.this)
|
||||
);
|
||||
} else {
|
||||
resultEntry.findViewById(R.id.self_check_result).setEnabled(false);
|
||||
}
|
||||
}
|
||||
viewGroup.addView(resultEntry);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package org.microg.tools.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
public abstract class AbstractSettingsActivity extends AppCompatActivity {
|
||||
protected boolean showHomeAsUp = false;
|
||||
protected int preferencesResource = 0;
|
||||
private ViewGroup customBarContainer;
|
||||
protected int customBarLayout = 0;
|
||||
protected SwitchBar switchBar;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.settings_activity);
|
||||
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
|
||||
|
||||
if (showHomeAsUp) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
switchBar = (SwitchBar) findViewById(R.id.switch_bar);
|
||||
|
||||
customBarContainer = (ViewGroup) findViewById(R.id.custom_bar);
|
||||
if (customBarLayout != 0) {
|
||||
customBarContainer.addView(getLayoutInflater().inflate(customBarLayout, customBarContainer, false));
|
||||
}
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.content_wrapper, getFragment())
|
||||
.commit();
|
||||
}
|
||||
|
||||
public void setCustomBarLayout(int layout) {
|
||||
customBarLayout = layout;
|
||||
if (customBarContainer != null) {
|
||||
customBarContainer.removeAllViews();
|
||||
customBarContainer.addView(getLayoutInflater().inflate(customBarLayout, customBarContainer, false));
|
||||
}
|
||||
}
|
||||
|
||||
public SwitchBar getSwitchBar() {
|
||||
return switchBar;
|
||||
}
|
||||
|
||||
public void replaceFragment(Fragment fragment) {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.addToBackStack("root")
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
.replace(R.id.content_wrapper, fragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
protected Fragment getFragment() {
|
||||
if (preferencesResource == 0) {
|
||||
throw new IllegalStateException("Neither preferencesResource given, nor overriden getFragment()");
|
||||
}
|
||||
ResourceSettingsFragment fragment = new ResourceSettingsFragment();
|
||||
Bundle b = new Bundle();
|
||||
b.putInt(ResourceSettingsFragment.EXTRA_PREFERENCE_RESOURCE, preferencesResource);
|
||||
fragment.setArguments(b);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
public abstract class AbstractSettingsFragment extends PreferenceFragmentCompat {
|
||||
private static final String TAG = AbstractSettingsFragment.class.getSimpleName();
|
||||
|
||||
private static final String DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG";
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
if (preference instanceof DialogPreference) {
|
||||
DialogFragment f = DialogPreference.DialogPreferenceCompatDialogFragment.newInstance(preference.getKey());
|
||||
f.setTargetFragment(this, 0);
|
||||
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
|
||||
} else {
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.PluralsRes;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
public class Condition {
|
||||
@DrawableRes
|
||||
private final int iconRes;
|
||||
private final Drawable icon;
|
||||
|
||||
@StringRes
|
||||
private final int titleRes;
|
||||
@PluralsRes
|
||||
private final int titlePluralsRes;
|
||||
private final CharSequence title;
|
||||
|
||||
@StringRes
|
||||
private final int summaryRes;
|
||||
@PluralsRes
|
||||
private final int summaryPluralsRes;
|
||||
private final CharSequence summary;
|
||||
|
||||
@StringRes
|
||||
private final int firstActionTextRes;
|
||||
@PluralsRes
|
||||
private final int firstActionPluralsRes;
|
||||
private final CharSequence firstActionText;
|
||||
private final View.OnClickListener firstActionListener;
|
||||
|
||||
@StringRes
|
||||
private final int secondActionTextRes;
|
||||
@PluralsRes
|
||||
private final int secondActionPluralsRes;
|
||||
private final CharSequence secondActionText;
|
||||
private final View.OnClickListener secondActionListener;
|
||||
|
||||
private final Evaluation evaluation;
|
||||
|
||||
private boolean evaluated = false;
|
||||
private boolean evaluating = false;
|
||||
private int evaluatedPlurals = -1;
|
||||
private boolean active;
|
||||
|
||||
Condition(Builder builder) {
|
||||
icon = builder.icon;
|
||||
title = builder.title;
|
||||
summary = builder.summary;
|
||||
firstActionText = builder.firstActionText;
|
||||
firstActionListener = builder.firstActionListener;
|
||||
secondActionText = builder.secondActionText;
|
||||
secondActionListener = builder.secondActionListener;
|
||||
summaryRes = builder.summaryRes;
|
||||
iconRes = builder.iconRes;
|
||||
firstActionTextRes = builder.firstActionTextRes;
|
||||
secondActionTextRes = builder.secondActionTextRes;
|
||||
titleRes = builder.titleRes;
|
||||
evaluation = builder.evaluation;
|
||||
titlePluralsRes = builder.titlePluralsRes;
|
||||
summaryPluralsRes = builder.summaryPluralsRes;
|
||||
firstActionPluralsRes = builder.firstActionPluralsRes;
|
||||
secondActionPluralsRes = builder.secondActionPluralsRes;
|
||||
}
|
||||
|
||||
View createView(final Context context, ViewGroup container) {
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View view = inflater.inflate(R.layout.condition_card, container, false);
|
||||
Drawable icon = getIcon(context);
|
||||
if (icon != null)
|
||||
((ImageView) view.findViewById(android.R.id.icon)).setImageDrawable(icon);
|
||||
((TextView) view.findViewById(android.R.id.title)).setText(getTitle(context));
|
||||
((TextView) view.findViewById(android.R.id.summary)).setText(getSummary(context));
|
||||
Button first = (Button) view.findViewById(R.id.first_action);
|
||||
first.setText(getFirstActionText(context));
|
||||
first.setOnClickListener(getFirstActionListener());
|
||||
CharSequence secondActionText = getSecondActionText(context);
|
||||
if (secondActionText != null) {
|
||||
Button second = (Button) view.findViewById(R.id.second_action);
|
||||
second.setText(secondActionText);
|
||||
second.setOnClickListener(getSecondActionListener());
|
||||
second.setVisibility(View.VISIBLE);
|
||||
}
|
||||
final View detailGroup = view.findViewById(R.id.detail_group);
|
||||
final ImageView expandIndicator = (ImageView) view.findViewById(R.id.expand_indicator);
|
||||
View.OnClickListener expandListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (detailGroup.getVisibility() == View.VISIBLE) {
|
||||
expandIndicator.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_expand_more, context.getTheme()));
|
||||
detailGroup.setVisibility(View.GONE);
|
||||
} else {
|
||||
expandIndicator.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_expand_less, context.getTheme()));
|
||||
detailGroup.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
};
|
||||
view.findViewById(R.id.collapsed_group).setOnClickListener(expandListener);
|
||||
expandIndicator.setOnClickListener(expandListener);
|
||||
view.setTag(this);
|
||||
return view;
|
||||
}
|
||||
|
||||
public Drawable getIcon(Context context) {
|
||||
if (iconRes != 0) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(), iconRes, context.getTheme());
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
public CharSequence getTitle(Context context) {
|
||||
if (titleRes != 0) {
|
||||
return context.getString(titleRes);
|
||||
}
|
||||
if (titlePluralsRes != 0) {
|
||||
return context.getResources().getQuantityString(titlePluralsRes, evaluatedPlurals);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
public CharSequence getSummary(Context context) {
|
||||
if (summaryRes != 0) {
|
||||
return context.getString(summaryRes);
|
||||
}
|
||||
if (summaryPluralsRes != 0) {
|
||||
return context.getResources().getQuantityString(summaryPluralsRes, evaluatedPlurals);
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
public View.OnClickListener getFirstActionListener() {
|
||||
return firstActionListener;
|
||||
}
|
||||
|
||||
public CharSequence getFirstActionText(Context context) {
|
||||
if (firstActionTextRes != 0) {
|
||||
return context.getString(firstActionTextRes);
|
||||
}
|
||||
if (firstActionPluralsRes != 0) {
|
||||
return context.getResources().getQuantityString(firstActionPluralsRes, evaluatedPlurals);
|
||||
}
|
||||
return firstActionText;
|
||||
}
|
||||
|
||||
public View.OnClickListener getSecondActionListener() {
|
||||
return secondActionListener;
|
||||
}
|
||||
|
||||
public CharSequence getSecondActionText(Context context) {
|
||||
if (secondActionTextRes != 0) {
|
||||
return context.getString(secondActionTextRes);
|
||||
}
|
||||
if (secondActionPluralsRes != 0) {
|
||||
return context.getResources().getQuantityString(secondActionPluralsRes, evaluatedPlurals);
|
||||
}
|
||||
return secondActionText;
|
||||
}
|
||||
|
||||
public synchronized boolean willBeEvaluating() {
|
||||
if (!evaluating && !evaluated && evaluation != null) {
|
||||
return evaluating = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEvaluated() {
|
||||
return evaluated || evaluation == null;
|
||||
}
|
||||
|
||||
public synchronized void evaluate(Context context) {
|
||||
active = evaluation == null || evaluation.isActive(context);
|
||||
evaluatedPlurals = evaluation.getPluralsCount();
|
||||
evaluated = true;
|
||||
evaluating = false;
|
||||
}
|
||||
|
||||
public boolean isActive(Context context) {
|
||||
if (!evaluated && evaluation != null) evaluate(context);
|
||||
return active;
|
||||
}
|
||||
|
||||
public void resetEvaluated() {
|
||||
this.evaluated = false;
|
||||
}
|
||||
|
||||
public static abstract class Evaluation {
|
||||
public abstract boolean isActive(Context context);
|
||||
|
||||
public int getPluralsCount() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
@DrawableRes
|
||||
private int iconRes;
|
||||
private Drawable icon;
|
||||
@StringRes
|
||||
private int titleRes;
|
||||
@PluralsRes
|
||||
private int titlePluralsRes;
|
||||
private CharSequence title;
|
||||
@StringRes
|
||||
private int summaryRes;
|
||||
@PluralsRes
|
||||
private int summaryPluralsRes;
|
||||
private CharSequence summary;
|
||||
@StringRes
|
||||
private int firstActionTextRes;
|
||||
@PluralsRes
|
||||
private int firstActionPluralsRes;
|
||||
private CharSequence firstActionText;
|
||||
private View.OnClickListener firstActionListener;
|
||||
@StringRes
|
||||
private int secondActionTextRes;
|
||||
@PluralsRes
|
||||
private int secondActionPluralsRes;
|
||||
private CharSequence secondActionText;
|
||||
private View.OnClickListener secondActionListener;
|
||||
private Evaluation evaluation;
|
||||
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder icon(Drawable val) {
|
||||
icon = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder icon(@DrawableRes int val) {
|
||||
iconRes = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder title(CharSequence val) {
|
||||
title = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder title(@StringRes int val) {
|
||||
titleRes = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder titlePlurals(@PluralsRes int val) {
|
||||
titlePluralsRes = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder summary(CharSequence val) {
|
||||
summary = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder summary(@StringRes int val) {
|
||||
summaryRes = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder summaryPlurals(@PluralsRes int val) {
|
||||
summaryPluralsRes = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder firstAction(CharSequence text, View.OnClickListener listener) {
|
||||
firstActionText = text;
|
||||
firstActionListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder firstAction(@StringRes int val, View.OnClickListener listener) {
|
||||
firstActionTextRes = val;
|
||||
firstActionListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder firstActionPlurals(@PluralsRes int val, View.OnClickListener listener) {
|
||||
firstActionPluralsRes = val;
|
||||
firstActionListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder secondAction(CharSequence text, View.OnClickListener listener) {
|
||||
secondActionText = text;
|
||||
secondActionListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder secondAction(@StringRes int val, View.OnClickListener listener) {
|
||||
secondActionTextRes = val;
|
||||
secondActionListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder secondActionPlurals(@PluralsRes int val, View.OnClickListener listener) {
|
||||
secondActionPluralsRes = val;
|
||||
secondActionListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder evaluation(Evaluation evaluation) {
|
||||
this.evaluation = evaluation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Condition build() {
|
||||
return new Condition(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
public class DialogPreference extends androidx.preference.DialogPreference implements PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
|
||||
|
||||
private static final String DIALOG_FRAGMENT_TAG =
|
||||
"android.support.v7.preference.PreferenceFragment.DIALOG";
|
||||
|
||||
public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public DialogPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public DialogPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
protected View onCreateDialogView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the dialog is dismissed and should be used to save data to
|
||||
* the {@link SharedPreferences}.
|
||||
*
|
||||
* @param positiveResult Whether the positive button was clicked (true), or
|
||||
* the negative button was clicked or the dialog was canceled (false).
|
||||
*/
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceDisplayDialog(PreferenceFragmentCompat caller, Preference pref) {
|
||||
DialogPreferenceCompatDialogFragment fragment = new DialogPreferenceCompatDialogFragment();
|
||||
fragment.setTargetFragment(caller, 0);
|
||||
fragment.show(caller.getFragmentManager(), DIALOG_FRAGMENT_TAG);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder view) {
|
||||
super.onBindViewHolder(view);
|
||||
|
||||
ViewGroup.LayoutParams layoutParams = view.findViewById(R.id.icon_frame).getLayoutParams();
|
||||
if (layoutParams instanceof LinearLayout.LayoutParams) {
|
||||
if (((LinearLayout.LayoutParams) layoutParams).leftMargin < 0) {
|
||||
((LinearLayout.LayoutParams) layoutParams).leftMargin = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DialogPreferenceCompatDialogFragment extends PreferenceDialogFragmentCompat {
|
||||
|
||||
@Override
|
||||
protected View onCreateDialogView(Context context) {
|
||||
if (getPreference() instanceof DialogPreference) {
|
||||
View view = ((DialogPreference) getPreference()).onCreateDialogView();
|
||||
if (view != null) return view;
|
||||
}
|
||||
return super.onCreateDialogView(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogClosed(boolean positiveResult) {
|
||||
if (getPreference() instanceof DialogPreference) {
|
||||
((DialogPreference) getPreference()).onDialogClosed(positiveResult);
|
||||
}
|
||||
}
|
||||
|
||||
public static DialogFragment newInstance(String key) {
|
||||
final DialogPreferenceCompatDialogFragment fragment = new DialogPreferenceCompatDialogFragment();
|
||||
final Bundle b = new Bundle(1);
|
||||
b.putString(ARG_KEY, key);
|
||||
fragment.setArguments(b);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
public class LongTextPreference extends Preference {
|
||||
|
||||
public LongTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public LongTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public LongTextPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public LongTextPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
TextView view = (TextView) holder.findViewById(android.R.id.summary);
|
||||
if (view != null) {
|
||||
view.setMaxLines(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.core.content.res.TypedArrayUtils;
|
||||
import androidx.preference.CheckBoxPreference;
|
||||
|
||||
public class RadioButtonPreference extends CheckBoxPreference {
|
||||
|
||||
public RadioButtonPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public RadioButtonPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
setWidgetLayoutResource(R.layout.preference_widget_radiobutton);
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
public RadioButtonPreference(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, TypedArrayUtils.getAttr(context, androidx.preference.R.attr.checkBoxPreferenceStyle,
|
||||
android.R.attr.checkBoxPreferenceStyle));
|
||||
}
|
||||
|
||||
public RadioButtonPreference(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ResourceSettingsFragment extends AbstractSettingsFragment {
|
||||
|
||||
public static final String EXTRA_PREFERENCE_RESOURCE = "preferencesResource";
|
||||
|
||||
protected int preferencesResource;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
|
||||
Bundle b = getArguments();
|
||||
if (b != null) {
|
||||
preferencesResource = b.getInt(EXTRA_PREFERENCE_RESOURCE, preferencesResource);
|
||||
}
|
||||
if (preferencesResource != 0) {
|
||||
addPreferencesFromResource(preferencesResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener,
|
||||
View.OnClickListener {
|
||||
|
||||
public static interface OnSwitchChangeListener {
|
||||
/**
|
||||
* Called when the checked state of the Switch has changed.
|
||||
*
|
||||
* @param switchView The Switch view whose state has changed.
|
||||
* @param isChecked The new checked state of switchView.
|
||||
*/
|
||||
void onSwitchChanged(SwitchCompat switchView, boolean isChecked);
|
||||
}
|
||||
|
||||
private final TextAppearanceSpan mSummarySpan;
|
||||
|
||||
private ToggleSwitch mSwitch;
|
||||
private TextView mTextView;
|
||||
private String mLabel;
|
||||
private String mSummary;
|
||||
|
||||
private ArrayList<OnSwitchChangeListener> mSwitchChangeListeners =
|
||||
new ArrayList<OnSwitchChangeListener>();
|
||||
|
||||
public SwitchBar(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SwitchBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.switch_bar, this);
|
||||
|
||||
mTextView = (TextView) findViewById(R.id.switch_text);
|
||||
if (SDK_INT > 16) {
|
||||
mTextView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
}
|
||||
mLabel = getResources().getString(androidx.appcompat.R.string.abc_capital_off);
|
||||
mSummarySpan = new TextAppearanceSpan(context, androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_Switch);
|
||||
updateText();
|
||||
|
||||
mSwitch = (ToggleSwitch) findViewById(R.id.switch_widget);
|
||||
// Prevent onSaveInstanceState() to be called as we are managing the state of the Switch
|
||||
// on our own
|
||||
mSwitch.setSaveEnabled(false);
|
||||
if (SDK_INT >= 16) {
|
||||
mSwitch.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
}
|
||||
|
||||
addOnSwitchChangeListener(new OnSwitchChangeListener() {
|
||||
@Override
|
||||
public void onSwitchChanged(SwitchCompat switchView, boolean isChecked) {
|
||||
setTextViewLabel(isChecked);
|
||||
}
|
||||
});
|
||||
|
||||
setOnClickListener(this);
|
||||
|
||||
// Default is hide
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void setTextViewLabel(boolean isChecked) {
|
||||
mLabel = getResources()
|
||||
.getString(isChecked ? androidx.appcompat.R.string.abc_capital_on : androidx.appcompat.R.string.abc_capital_off);
|
||||
updateText();
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
mSummary = summary;
|
||||
updateText();
|
||||
}
|
||||
|
||||
private void updateText() {
|
||||
if (TextUtils.isEmpty(mSummary)) {
|
||||
mTextView.setText(mLabel);
|
||||
return;
|
||||
}
|
||||
final SpannableStringBuilder ssb = new SpannableStringBuilder(mLabel).append('\n');
|
||||
final int start = ssb.length();
|
||||
ssb.append(mSummary);
|
||||
ssb.setSpan(mSummarySpan, start, ssb.length(), 0);
|
||||
mTextView.setText(ssb);
|
||||
}
|
||||
|
||||
public void setChecked(boolean checked) {
|
||||
setTextViewLabel(checked);
|
||||
mSwitch.setChecked(checked);
|
||||
}
|
||||
|
||||
public void setCheckedInternal(boolean checked) {
|
||||
setTextViewLabel(checked);
|
||||
mSwitch.setCheckedInternal(checked);
|
||||
}
|
||||
|
||||
public boolean isChecked() {
|
||||
return mSwitch.isChecked();
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
mTextView.setEnabled(enabled);
|
||||
mSwitch.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public final ToggleSwitch getSwitch() {
|
||||
return mSwitch;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (!isShowing()) {
|
||||
setVisibility(View.VISIBLE);
|
||||
mSwitch.setOnCheckedChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
if (isShowing()) {
|
||||
setVisibility(View.GONE);
|
||||
mSwitch.setOnCheckedChangeListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return (getVisibility() == View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final boolean isChecked = !mSwitch.isChecked();
|
||||
setChecked(isChecked);
|
||||
}
|
||||
|
||||
public void propagateChecked(boolean isChecked) {
|
||||
final int count = mSwitchChangeListeners.size();
|
||||
for (int n = 0; n < count; n++) {
|
||||
mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
propagateChecked(isChecked);
|
||||
}
|
||||
|
||||
public void addOnSwitchChangeListener(OnSwitchChangeListener listener) {
|
||||
if (mSwitchChangeListeners.contains(listener)) {
|
||||
throw new IllegalStateException("Cannot add twice the same OnSwitchChangeListener");
|
||||
}
|
||||
mSwitchChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeOnSwitchChangeListener(OnSwitchChangeListener listener) {
|
||||
if (!mSwitchChangeListeners.contains(listener)) {
|
||||
throw new IllegalStateException("Cannot remove OnSwitchChangeListener");
|
||||
}
|
||||
mSwitchChangeListeners.remove(listener);
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
boolean checked;
|
||||
boolean visible;
|
||||
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called from {@link #CREATOR}
|
||||
*/
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
checked = (Boolean) in.readValue(Boolean.class.getClassLoader());
|
||||
visible = (Boolean) in.readValue(Boolean.class.getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeValue(checked);
|
||||
out.writeValue(visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SwitchBar.SavedState{"
|
||||
+ Integer.toHexString(System.identityHashCode(this))
|
||||
+ " checked=" + checked
|
||||
+ " visible=" + visible + "}";
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR
|
||||
= new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
|
||||
SavedState ss = new SavedState(superState);
|
||||
ss.checked = mSwitch.isChecked();
|
||||
ss.visible = isShowing();
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState ss = (SavedState) state;
|
||||
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
|
||||
mSwitch.setCheckedInternal(ss.checked);
|
||||
setTextViewLabel(ss.checked);
|
||||
setVisibility(ss.visible ? View.VISIBLE : View.GONE);
|
||||
mSwitch.setOnCheckedChangeListener(ss.visible ? this : null);
|
||||
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
|
||||
public abstract class SwitchBarResourceSettingsFragment extends ResourceSettingsFragment implements SwitchBar.OnSwitchChangeListener {
|
||||
protected SwitchBar switchBar;
|
||||
private SwitchCompat switchCompat;
|
||||
private boolean listenerSetup = false;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
// switchBar = activity.getSwitchBar();
|
||||
// switchBar.show();
|
||||
// switchCompat = switchBar.getSwitch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
// switchBar.hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!listenerSetup) {
|
||||
// switchBar.addOnSwitchChangeListener(this);
|
||||
listenerSetup = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (listenerSetup) {
|
||||
// switchBar.removeOnSwitchChangeListener(this);
|
||||
listenerSetup = false;
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwitchChanged(SwitchCompat switchView, boolean isChecked) {
|
||||
if (switchView == switchCompat) {
|
||||
onSwitchBarChanged(isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void onSwitchBarChanged(boolean isChecked);
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.tools.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public class ToggleSwitch extends SwitchCompat {
|
||||
|
||||
private ToggleSwitch.OnBeforeCheckedChangeListener mOnBeforeListener;
|
||||
|
||||
public interface OnBeforeCheckedChangeListener {
|
||||
boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked);
|
||||
}
|
||||
|
||||
public ToggleSwitch(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ToggleSwitch(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public ToggleSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setOnBeforeCheckedChangeListener(OnBeforeCheckedChangeListener listener) {
|
||||
mOnBeforeListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked) {
|
||||
if (mOnBeforeListener != null
|
||||
&& mOnBeforeListener.onBeforeCheckedChanged(this, checked)) {
|
||||
return;
|
||||
}
|
||||
super.setChecked(checked);
|
||||
}
|
||||
|
||||
public void setCheckedInternal(boolean checked) {
|
||||
super.setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/ripple_material_dark">
|
||||
<item android:drawable="@color/switchbar_background_color"/>
|
||||
</ripple>
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<selector/>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~ Copyright (C) 2015-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M12.0,8.0l-6.0,6.0 1.41,1.41L12.0,10.83l4.59,4.58L18.0,14.0z"/>
|
||||
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~ Copyright (C) 2015-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/>
|
||||
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<color xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/switchbar_background_color"/>
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dip"
|
||||
android:background="?android:attr/listDivider"/>
|
||||
|
||||
<TextView android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dip"
|
||||
android:paddingBottom="8dip"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingTop="8dip"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~ Copyright (C) 2015-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:baselineAligned="false"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/icon_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minWidth="56dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="4dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:maxHeight="48dp"
|
||||
app:maxWidth="48dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignStart="@android:id/title"
|
||||
android:layout_below="@android:id/title"
|
||||
android:maxLines="10"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Preference should place its actual preference widget here. -->
|
||||
<LinearLayout
|
||||
android:id="@android:id/widget_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:padding="10dp"
|
||||
android:src="@android:drawable/ic_dialog_alert"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/about_root_title"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||
android:textColor="?attr/colorAccent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:visibility="gone"
|
||||
android:text="@string/about_root_summary"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:textColor="?attr/colorAccent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about_version"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/about_root_version"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingTop="16dip"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:text="@string/about_root_libraries"
|
||||
android:textColor="?attr/colorAccent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<!--
|
||||
~ Copyright (C) 2014 The Android Open Source Project
|
||||
~ Copyright (C) 2014-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<RelativeLayout android:id="@+id/app_bar"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@drawable/switchbar_background"
|
||||
android:clickable="true"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="end"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginLeft="72dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:gravity="start"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~ Copyright (C) 2015-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="0.25dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorAccent"
|
||||
android:clickable="true"
|
||||
android:elevation="2dp"
|
||||
android:focusable="true"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingLeft="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/collapsed_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="36dp"
|
||||
android:layout_marginRight="36dp"
|
||||
android:src="@android:drawable/ic_dialog_alert"
|
||||
app:tint="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Test Condition"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/expand_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_expand_more"
|
||||
app:tint="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/detail_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="60dp"
|
||||
android:paddingLeft="60dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha=".7"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="This condition just exists for testing. This is a summary describing it."
|
||||
android:textAppearance="?attr/textAppearanceListItemSmall"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.25dp"
|
||||
android:background="@android:color/white" />
|
||||
|
||||
<androidx.appcompat.widget.ButtonBarLayout
|
||||
android:id="@+id/buttonBar"
|
||||
style="?attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/first_action"
|
||||
style="?attr/buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingLeft="0dp"
|
||||
android:text="Fix it!"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/second_action"
|
||||
style="?attr/buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.appcompat.widget.ButtonBarLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/condition_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<RadioButton android:id="@android:id/checkbox"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:clickable="false"
|
||||
android:focusable="false"/>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/self_check_root"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/self_check_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
android:textColor="?android:textColorPrimary"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/self_check_resolution"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textColor="?android:textColorSecondary"/>
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/self_check_result"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:gravity="right|center_vertical"
|
||||
android:paddingTop="5dp"/>
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingTop="16dip"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textColor="?attr/colorAccent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/group_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="?attr/actionBarTheme" />
|
||||
|
||||
<org.microg.tools.ui.SwitchBar
|
||||
android:id="@+id/switch_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/switchbar_background_color"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/custom_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2014 The Android Open Source Project
|
||||
~ Copyright (C) 2014-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/switch_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginLeft="72dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="@string/v7_preference_on"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title.Inverse"/>
|
||||
|
||||
<org.microg.tools.ui.ToggleSwitch
|
||||
android:id="@+id/switch_widget"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:background="@null"/>
|
||||
|
||||
</merge>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2020 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG Team</string>
|
||||
|
||||
<string name="about_version_str">Версія %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Ўсе правы абаронены.</string>
|
||||
|
||||
<string name="prefcat_setup">Ўсталяваць</string>
|
||||
|
||||
<string name="self_check_title">Працаздольнасць</string>
|
||||
<string name="self_check_desc">Праверце, ці правільна настроена сістэма для выкарыстання microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Правы доступу прадастаўленыя</string>
|
||||
<string name="self_check_name_permission">%1$s:</string>
|
||||
<string name="self_check_resolution_permission">Краніце, каб даць дазвол. Адсутны дазвол можа прывесці да некарэктнай працы прыкладання.</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demo</string>
|
||||
<string name="about_root_summary">Падрабязнасці</string>
|
||||
<string name="about_root_version">Версія v0.1.0</string>
|
||||
<string name="about_root_libraries">Выкарыстоўваемыя бібліятэкі</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG Team</string>
|
||||
|
||||
<string name="about_version_str">Verze %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Všechna práva vyhrazena.</string>
|
||||
|
||||
<string name="prefcat_setup">Nastavení</string>
|
||||
|
||||
<string name="self_check_title">Vlastní kontrola</string>
|
||||
<string name="self_check_desc">Zkontrolovat, zda je systém správně nastaven pro používání microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Udělená oprávnění</string>
|
||||
<string name="self_check_name_permission">Oprávnění „%1$s“:</string>
|
||||
<string name="self_check_resolution_permission">Klepěte sem pro udělení oprávnění. Neudělení oprávnění může mít za výsledek nesprávně fungující aplikace.</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demo</string>
|
||||
<string name="about_root_summary">Souhrn</string>
|
||||
<string name="about_root_version">Verze v0.1.0</string>
|
||||
<string name="about_root_libraries">Zahrnuté knihovny</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG Team</string>
|
||||
|
||||
<string name="about_version_str">Version %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Alle Rechte vorbehalten.</string>
|
||||
|
||||
<string name="prefcat_setup">Einrichtung</string>
|
||||
|
||||
<string name="self_check_title">Selbstprüfung</string>
|
||||
<string name="self_check_desc">Prüft, ob das System zur Nutzung von microG konfiguriert ist.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Erteilte Berechtigungen</string>
|
||||
<string name="self_check_name_permission">%1$s:</string>
|
||||
<string name="self_check_resolution_permission">Hier drücken, um die Berechtigung zu erteilen. Verweigern einer Berechtigung kann zu Fehlverhalten in anderen Anwendungen führen.</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demo</string>
|
||||
<string name="about_root_summary">Zusammenfassung</string>
|
||||
<string name="about_root_version">Version v0.1.0</string>
|
||||
<string name="about_root_libraries">Genutzte Bibliotheken</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2020 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">Herramientas de IU de microG</string>
|
||||
<string name="lib_license">Apache License 2.0, Equipo de microG</string>
|
||||
|
||||
<string name="about_version_str">Versión %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Todos los derechos reservados.</string>
|
||||
|
||||
<string name="prefcat_setup">Configuración</string>
|
||||
|
||||
<string name="self_check_title">Autocomprobación</string>
|
||||
<string name="self_check_desc">Comprueba si el sistema está correctamente configurado para usar microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Permisos concedidos</string>
|
||||
<string name="self_check_name_permission">Permiso para %1$s:</string>
|
||||
<string name="self_check_resolution_permission">Toque aquí para conceder el permiso. No conceder el permiso puede resultar en comportamientos incorrectos de las aplicaciones.</string>
|
||||
|
||||
<string name="about_root_title">Demo de IU de microG</string>
|
||||
<string name="about_root_summary">Resumen</string>
|
||||
<string name="about_root_version">Versión v0.1.0</string>
|
||||
<string name="about_root_libraries">Librerías incluidas</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG Team</string>
|
||||
|
||||
<string name="about_version_str">Bersyon %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Nakalaan ang lahat ng karapatan.</string>
|
||||
|
||||
<string name="prefcat_setup">Setup</string>
|
||||
|
||||
<string name="self_check_title">Sariling Pagsusuri</string>
|
||||
<string name="self_check_desc">Tignan kung tamang na-set up ang system para magamit ang microG</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Mga ibinigay na pahintulot</string>
|
||||
<string name="self_check_name_permission">Pahintulot na %1$s:</string>
|
||||
<string name="self_check_name_permission_interact_across_profiles">Pahintulot na mag-interact sa profile sa trabaho:</string>
|
||||
<string name="self_check_resolution_permission">Pindutin dito para ibigay ang pahintulot. Ang hindi pagbigay ng pahintulot ay maaaring magresulta sa maling pag-uugali ng mga application.</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demo</string>
|
||||
<string name="about_root_summary">Pangkalahatang ideya</string>
|
||||
<string name="about_root_version">Bersyon v0.1.0</string>
|
||||
<string name="about_root_libraries">Mga kasamang library</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG Team</string>
|
||||
|
||||
<string name="about_version_str">Version %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Tous droits réservés.</string>
|
||||
|
||||
<string name="prefcat_setup">Configuration</string>
|
||||
|
||||
<string name="self_check_title">Auto-vérification.</string>
|
||||
<string name="self_check_desc">Vérifier si le système est correctement configuré pour utiliser microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Autorisations accordées</string>
|
||||
<string name="self_check_name_permission">Autorisation à %1$s :</string>
|
||||
<string name="self_check_resolution_permission">Touchez ici pour accorder l’autorisation. Des applications peuvent mal se comporter si vous ne le faites pas.</string>
|
||||
|
||||
<string name="about_root_version">Version v0.1.0</string>
|
||||
<string name="about_root_libraries">Librairies incluses</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">Strumenti interfaccia utente di microG</string>
|
||||
<string name="lib_license">Licenza Apache 2.0, team di microG</string>
|
||||
|
||||
<string name="about_version_str">Versione %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Tutti i diritti sono riservati.</string>
|
||||
|
||||
<string name="prefcat_setup">Configurazione</string>
|
||||
|
||||
<string name="self_check_title">Controllo dei problemi</string>
|
||||
<string name="self_check_desc">Verifica se il sistema è correttamente configurato per utilizzare microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Autorizzazioni concesse</string>
|
||||
<string name="self_check_name_permission">%1$s:</string>
|
||||
<string name="self_check_resolution_permission">Tocca qui per concedere l\'autorizzazione. Negare l\'autorizzazione può comportare il funzionamento anomalo di altre applicazioni.</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demo</string>
|
||||
<string name="about_root_summary">Riepilogo</string>
|
||||
<string name="about_root_version">Versione v0.1.0</string>
|
||||
<string name="about_root_libraries">Librerie incluse</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Licenza Apache 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UIツール</string>
|
||||
<string name="lib_license">Apache License 2.0, microGチーム</string>
|
||||
|
||||
<string name="about_version_str">バージョン %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">All rights reserved.</string>
|
||||
|
||||
<string name="prefcat_setup">設定</string>
|
||||
|
||||
<string name="self_check_title">セルフチェック</string>
|
||||
<string name="self_check_desc">システムがmicroGを使用するよう正しく設定されているか確認します。</string>
|
||||
|
||||
<string name="self_check_cat_permissions">権限付与</string>
|
||||
<string name="self_check_name_permission">%1$sの権限:</string>
|
||||
<string name="self_check_resolution_permission">ここをタップして権限を付与してください。 権限を付与しないと、アプリが正しく動作しない可能性があります。</string>
|
||||
|
||||
<string name="about_root_title">microG UIデモ</string>
|
||||
<string name="about_root_summary">概要</string>
|
||||
<string name="about_root_version">バージョン v0.1.0</string>
|
||||
<string name="about_root_libraries">使用されているライブラリ</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">Narzędzia UI microG</string>
|
||||
<string name="lib_license">Licencja Apache 2.0, Zespół microG</string>
|
||||
|
||||
<string name="about_version_str">Wersja %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Wszelkie prawa zastrzeżone.</string>
|
||||
|
||||
<string name="prefcat_setup">Ustawienia</string>
|
||||
|
||||
<string name="self_check_title">Samo-sprawdzenie</string>
|
||||
<string name="self_check_desc">Sprawdza, czy system jest poprawnie skonfigurowany do korzystania z microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Udzielono uprawnień</string>
|
||||
<string name="self_check_name_permission">Uprawnienie do %1$s:</string>
|
||||
<string name="self_check_resolution_permission">Stuknij, aby udzielić uprawnienia. Nieudzielenie uprawnienia może powodować problemy z aplikacjami.</string>
|
||||
|
||||
<string name="about_root_title">Demo microG UI</string>
|
||||
<string name="about_root_summary">Podsumowanie</string>
|
||||
<string name="about_root_version">Wersja v0.1.0</string>
|
||||
<string name="about_root_libraries">Użyte biblioteki</string>
|
||||
|
||||
<string name="about_android_support_license">Licencja Apache 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">Ferramentas de UI do microG</string>
|
||||
<string name="lib_license">Licença "Apache 2.0", Equipe do microG</string>
|
||||
|
||||
<string name="about_version_str">Versão %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Todos os direitos reservados.</string>
|
||||
|
||||
<string name="prefcat_setup">Configuração</string>
|
||||
|
||||
<string name="self_check_title">Auto-verificação</string>
|
||||
<string name="self_check_desc">Verifique se o sistema está configurado corretamente para usar o microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Permissões concedidas</string>
|
||||
<string name="self_check_name_permission">Permissão para %1$s:</string>
|
||||
<string name="self_check_name_permission_interact_across_profiles">Permissão para interagir com perfil de trabalho:</string>
|
||||
<string name="self_check_resolution_permission">Toque aqui para conceder esta permissão. Não conceder esta permissão pode resultar em aplicativos com mal comportamento.</string>
|
||||
|
||||
<string name="about_root_title">Demo de UI do microG</string>
|
||||
<string name="about_root_summary">Resumo</string>
|
||||
<string name="about_root_version">Versão v0.1.0</string>
|
||||
<string name="about_root_libraries">Bibliotecas inclusas</string>
|
||||
|
||||
<string name="about_android_support_v4">Biblioteca de suporte v4</string>
|
||||
<string name="about_android_support_v7_appcompat">Biblioteca de suporte v7 appcompat</string>
|
||||
<string name="about_android_support_v7_preference">Biblioteca de suporte v7 preferences</string>
|
||||
<string name="about_android_support_license">Licença "Apache 2.0", Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">Instrumente microG UI</string>
|
||||
<string name="lib_license">Apache License 2.0, Echipa microG</string>
|
||||
|
||||
<string name="about_version_str">Versiune %1$s</string>
|
||||
<string name="about_default_license">Toate drepturile rezervate.</string>
|
||||
|
||||
<string name="prefcat_setup">Configurare</string>
|
||||
|
||||
<string name="self_check_title">Autoverificare</string>
|
||||
<string name="self_check_desc">Verifică dacă sistemul este configurat corect pentru a utiliza microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Permisiunile acordate</string>
|
||||
<string name="self_check_name_permission">Permisiunea %1$s:</string>
|
||||
<string name="self_check_name_permission_interact_across_profiles">Permisiunea de a interacționa cu profilul de serviciu:</string>
|
||||
<string name="self_check_resolution_permission">Atinge aici pentru a acorda permisiunea. Neacordarea permisiunii poate duce la un comportament necorespunzător al aplicațiilor.</string>
|
||||
|
||||
<string name="about_root_title">Demo microG UI</string>
|
||||
<string name="about_root_summary">Sumar</string>
|
||||
<string name="about_root_version">Versiune v0.1.0</string>
|
||||
<string name="about_root_libraries">Biblioteci incluse</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2020 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG Team</string>
|
||||
|
||||
<string name="about_version_str">Версия %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Все права защищены.</string>
|
||||
|
||||
<string name="prefcat_setup">Установить</string>
|
||||
|
||||
<string name="self_check_title">Работоспособность</string>
|
||||
<string name="self_check_desc">Проверьте, правильно ли настроена система для использования microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Права доступа предоставлены</string>
|
||||
<!-- it's better if the next line stays like this -->
|
||||
<string name="self_check_name_permission">%1$s:</string>
|
||||
<string name="self_check_resolution_permission">Коснитесь, чтобы предоставить разрешение. Отсутствующее разрешение может привести к некорректной работе приложения.</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demo</string>
|
||||
<string name="about_root_summary">Подробности</string>
|
||||
<string name="about_root_version">Версия v0.1.0</string>
|
||||
<string name="about_root_libraries">Используемые библиотеки</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="prefcat_setup">Поставка</string>
|
||||
|
||||
<string name="self_check_title">микроГ самопровера</string>
|
||||
<string name="self_check_desc">Провера исправности подешавања система за коришћење микроГ услуга.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Дозволе одобрене</string>
|
||||
<string name="self_check_name_permission">Дозволе за %1$s:</string>
|
||||
<string name="self_check_resolution_permission">Тапните овде да одобрите дозволе. Не одобравање дозвола може да резултира чудним понашањем апликација.</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Araçları</string>
|
||||
<string name="lib_license">Apache Lisansı 2.0, microG Ekibi</string>
|
||||
|
||||
<string name="about_version_str">Sürüm %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Tüm hakları saklıdır.</string>
|
||||
|
||||
<string name="prefcat_setup">Kurulum</string>
|
||||
|
||||
<string name="self_check_title">Çalışma durumu</string>
|
||||
<string name="self_check_desc">microG\'yi kullanmak için sistemin doğru şekilde ayarlanıp ayarlanmadığını kontrol edin.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Verilen izinler</string>
|
||||
<string name="self_check_name_permission">%1$s izni:</string>
|
||||
<string name="self_check_resolution_permission">Buraya dokunarak izin verin. İznin verilmemesi uygulamaların hatalı davranmasına neden olabilir.</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demosu</string>
|
||||
<string name="about_root_summary">Özet</string>
|
||||
<string name="about_root_version">Sürüm v0.1.0</string>
|
||||
<string name="about_root_libraries">Dahil olan kütüphaneler</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache Lisansı 2.0, Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG Team</string>
|
||||
|
||||
<string name="about_version_str">Версія %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Всі права захищено.</string>
|
||||
|
||||
<string name="prefcat_setup">Налаштування</string>
|
||||
|
||||
<string name="self_check_title">Само-тестування</string>
|
||||
<string name="self_check_desc">Перевірка, чи належним чином система використовує microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Доступ надано</string>
|
||||
<string name="self_check_name_permission">Доступ до %1$s:</string>
|
||||
<string name="self_check_resolution_permission">Торкніться, аби надати доступ. Без доступу не гарантується робота додатку належним чином.</string>
|
||||
|
||||
<string name="about_root_title">Демонстрація microG UI</string>
|
||||
<string name="about_root_summary">Резюме</string>
|
||||
<string name="about_root_version">Версія v0.1.0</string>
|
||||
<string name="about_root_libraries">Використані бібліотеки</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="lib_name">Công cụ giao diện microG</string>
|
||||
<string name="lib_license">Giấy phép Apache 2.0, nhóm microG</string>
|
||||
|
||||
<string name="about_version_str">Phiên bản %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">Đã đăng ký bản quyền.</string>
|
||||
|
||||
<string name="prefcat_setup">Thiết lập</string>
|
||||
|
||||
<string name="self_check_title">Tự kiểm tra</string>
|
||||
<string name="self_check_desc">Kiểm tra xem hệ thống của bạn đã sẵn sàng để dùng microG chưa.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Cấp quyền</string>
|
||||
<string name="self_check_name_permission">Quyền %1$s:</string>
|
||||
<string name="self_check_name_permission_interact_across_profiles">Quyền tương tác với hồ sơ công việc:</string>
|
||||
<string name="self_check_resolution_permission">Chạm vào đây để cấp quyền. Nếu không, một số ứng dụng có thể chạy không ổn định.</string>
|
||||
|
||||
<string name="about_root_title">Demo giao diện microG</string>
|
||||
<string name="about_root_summary">Giới thiệu</string>
|
||||
<string name="about_root_version">Phiên bản v0.1.0</string>
|
||||
<string name="about_root_libraries">Thư viện đi kèm</string>
|
||||
|
||||
<string name="about_android_support_v4">Thư viện hỗ trợ v4</string>
|
||||
<string name="about_android_support_v7_appcompat">Thư viện hỗ trợ v7 appcompat</string>
|
||||
<string name="about_android_support_v7_preference">Thư viện hỗ trợ v7 preference</string>
|
||||
<string name="about_android_support_license">Giấy phép Apache 2.0, Dự án mã nguồn mở Android</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="about_version_str">版本 %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">保留所有权利。</string>
|
||||
<string name="prefcat_setup">设置</string>
|
||||
<string name="self_check_title">自我检查</string>
|
||||
<string name="self_check_desc">确认系统是否已正确配置成 microG 可正常使用的状态</string>
|
||||
<string name="self_check_cat_permissions">已授予权限</string>
|
||||
<string name="self_check_name_permission">%1$s的权限:</string>
|
||||
<string name="self_check_name_permission_interact_across_profiles">与工作配置文件交互的权限:</string>
|
||||
<string name="self_check_resolution_permission">点击授予权限。不授予权限可能导致应用工作异常。</string>
|
||||
<string name="about_root_libraries">包含的库</string>
|
||||
<string name="about_root_summary">概要</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG 團隊</string>
|
||||
|
||||
<string name="about_version_str">版本 %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">保留所有權利。</string>
|
||||
|
||||
<string name="prefcat_setup">設定</string>
|
||||
|
||||
<string name="self_check_title">自我檢查</string>
|
||||
<string name="self_check_desc">確認系統是否已正確配置以使用 microG。</string>
|
||||
|
||||
<string name="self_check_cat_permissions">允許權限</string>
|
||||
<string name="self_check_name_permission">允許 %1$s 的權限:</string>
|
||||
<string name="self_check_resolution_permission">點擊這裡以授權。未授權可能影響程式正常運作。</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demo</string>
|
||||
<string name="about_root_summary">大綱</string>
|
||||
<string name="about_root_version">版本 v0.1.0</string>
|
||||
<string name="about_root_libraries">包含的函式庫</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<color name="settings_theme_primary">#ff263238</color>
|
||||
<color name="settings_theme_primary_dark">#ff21272b</color>
|
||||
<color name="settings_theme_accent">#ff009688</color>
|
||||
|
||||
<color name="switchbar_background_color">#ff37474f</color>
|
||||
<color name="switch_accent_color">#ff7fcac3</color>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="lib_name">microG UI Tools</string>
|
||||
<string name="lib_license">Apache License 2.0, microG Team</string>
|
||||
|
||||
<string name="about_version_str">Version %1$s</string>
|
||||
<string name="about_name_version_str">%1$s %2$s</string>
|
||||
<string name="about_default_license">All rights reserved.</string>
|
||||
|
||||
<string name="prefcat_setup">Setup</string>
|
||||
|
||||
<string name="self_check_title">Self-Check</string>
|
||||
<string name="self_check_desc">Check if the system is correctly set up to use microG.</string>
|
||||
|
||||
<string name="self_check_cat_permissions">Permissions granted</string>
|
||||
<string name="self_check_name_permission">Permission to %1$s:</string>
|
||||
<string name="self_check_name_permission_interact_across_profiles">Permission to interact with work profile:</string>
|
||||
<string name="self_check_resolution_permission">Touch here to grant permission. Not granting the permission can result in misbehaving applications.</string>
|
||||
|
||||
<string name="about_root_title">microG UI Demo</string>
|
||||
<string name="about_root_summary">Summary</string>
|
||||
<string name="about_root_version">Version v0.1.0</string>
|
||||
<string name="about_root_libraries">Included libraries</string>
|
||||
|
||||
<string name="about_android_support_v4">v4 Support Library</string>
|
||||
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
|
||||
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
|
||||
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
|
||||
</resources>
|
||||
12
play-services-core/multidex-keep.pro
Normal file
12
play-services-core/multidex-keep.pro
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Make sure maps is in the primary dex file
|
||||
-keep class com.google.android.gms.maps.** { *; }
|
||||
-keep class org.microg.gms.maps.** { *; }
|
||||
-keep class com.mapbox.** { *; }
|
||||
-keep class org.oscim.** { *; }
|
||||
|
||||
# Keep Dynamite Loader in the primary dex file otherwise it will error out on legacy Android versions
|
||||
-keep class com.google.android.gms.chimera.container.DynamiteLoaderImpl { *; }
|
||||
|
||||
# Keep Conscrypt in the primary dex file otherwise it will error out on legacy Android versions
|
||||
-keep class com.google.android.gms.common.security.ProviderInstallerImpl { *; }
|
||||
-keep class com.google.android.gms.org.conscrypt.** { *; }
|
||||
1
play-services-core/proguard-rules.pro
vendored
Symbolic link
1
play-services-core/proguard-rules.pro
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../proguard.flags
|
||||
62
play-services-core/src/huawei/AndroidManifest.xml
Normal file
62
play-services-core/src/huawei/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<!--
|
||||
~ SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application>
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.checkin_enable_service"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.gcm_enable_mcs_service"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.auth_manager_visible"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.auth_include_android_id"
|
||||
android:value="false" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.auth_strip_device_name"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.auth_two_step_verification"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.droidguard_enabled"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.safetynet_enabled"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_billing"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_licensing_purchase_free_apps"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_licensing"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_asset_delivery"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_device_sync"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_split_install"
|
||||
android:value="false" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.game_allow_create_player"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.allow_upload_game_played"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_apps_install"
|
||||
android:value="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
62
play-services-core/src/huaweilh/AndroidManifest.xml
Normal file
62
play-services-core/src/huaweilh/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<!--
|
||||
~ SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application>
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.checkin_enable_service"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.gcm_enable_mcs_service"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.auth_manager_visible"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.auth_include_android_id"
|
||||
android:value="false" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.auth_strip_device_name"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.auth_two_step_verification"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.droidguard_enabled"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.safetynet_enabled"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_billing"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="org.microg.gms.settings.vending_licensing"
|
||||
android:value="true" />
|
||||
|
||||
<activity-alias
|
||||
android:name="org.microg.gms.ui.SettingsActivity"
|
||||
android:targetActivity="org.microg.gms.ui.MainSettingsActivity"
|
||||
tools:node="remove" />
|
||||
|
||||
<activity-alias
|
||||
android:name="org.lighthouse.SettingsActivity"
|
||||
android:icon="@mipmap/ic_app_settings"
|
||||
android:label="@string/gms_settings_name"
|
||||
android:roundIcon="@mipmap/ic_app_settings"
|
||||
android:process=":ui"
|
||||
android:targetActivity="org.microg.gms.ui.MainSettingsActivity"
|
||||
android:taskAffinity="org.microg.gms.settings"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
</application>
|
||||
</manifest>
|
||||
1331
play-services-core/src/main/AndroidManifest.xml
Normal file
1331
play-services-core/src/main/AndroidManifest.xml
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.analytics.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
public class AnalyticsService extends Service {
|
||||
private static final String TAG = "GmsAnalyticsSvc";
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
Log.d(TAG, "onBind: " + intent);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import org.microg.gms.auth.AuthManagerServiceImpl;
|
||||
|
||||
public class GetToken extends Service {
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new AuthManagerServiceImpl(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
public class TokenActivity extends Activity {
|
||||
private static final String TAG = "GmsAuthTokenActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Bundle extras = getIntent().getExtras();
|
||||
extras.get("KEY");
|
||||
Log.d(TAG, extras.toString());
|
||||
/*AccountManager accountManager = AccountManager.get(this);
|
||||
accountManager.getAuthToken(new Account(extras.getString("authAccount"), "com.google"), extras.getString("service"), extras.getBundle("callerExtras"), this, new AccountManagerCallback<Bundle>() {
|
||||
@Override
|
||||
public void run(AccountManagerFuture<Bundle> future) {
|
||||
try {
|
||||
Bundle result = future.getResult();
|
||||
if (result != null) {
|
||||
result.get("KEY");
|
||||
Log.d("TokenActivity", result.toString());
|
||||
} else {
|
||||
Log.d("TokenActivity", "null-result");
|
||||
}
|
||||
} catch (OperationCanceledException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (AuthenticatorException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, new Handler(getMainLooper()));*/
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2025 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.google.android.gms.chimera;
|
||||
|
||||
import static android.os.Build.CPU_ABI;
|
||||
import static android.os.Build.SUPPORTED_32_BIT_ABIS;
|
||||
import static android.os.Build.SUPPORTED_64_BIT_ABIS;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.chimera.container.DynamiteContext;
|
||||
import com.google.android.gms.chimera.container.DynamiteModuleInfo;
|
||||
import com.google.android.gms.chimera.container.FilteredClassLoader;
|
||||
|
||||
import org.microg.gms.common.Constants;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import dalvik.system.PathClassLoader;
|
||||
|
||||
public class DynamiteContextFactory {
|
||||
private static final String TAG = "DynamiteContextFactory";
|
||||
private static final Map<String, DynamiteContext> sContextCache = new WeakHashMap<>();
|
||||
// WeakHashMap cannot be used, and there is a high probability that it will be recycled, causing ClassLoader to be rebuilt
|
||||
private static final Map<String, ClassLoader> sClassLoaderCache = new HashMap<>();
|
||||
|
||||
public static DynamiteContext createDynamiteContext(String moduleId, Context originalContext) {
|
||||
if (originalContext == null) {
|
||||
Log.w(TAG, "create <DynamiteContext> Original context is null");
|
||||
return null;
|
||||
}
|
||||
String cacheKey = moduleId + "-" + originalContext.getPackageName();
|
||||
synchronized (sContextCache) {
|
||||
DynamiteContext cached = sContextCache.get(cacheKey);
|
||||
if (cached != null) {
|
||||
Log.d(TAG, "Using cached DynamiteContext for cacheKey: " + cacheKey);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
try {
|
||||
DynamiteModuleInfo moduleInfo = new DynamiteModuleInfo(moduleId);
|
||||
Context gmsContext = originalContext.createPackageContext(Constants.GMS_PACKAGE_NAME, 0);
|
||||
Context originalAppContext = originalContext.getApplicationContext();
|
||||
|
||||
DynamiteContext dynamiteContext;
|
||||
if (originalAppContext == null || originalAppContext == originalContext) {
|
||||
dynamiteContext = new DynamiteContext(moduleInfo, originalContext, gmsContext, null);
|
||||
} else {
|
||||
dynamiteContext = new DynamiteContext(moduleInfo, originalContext, gmsContext, new DynamiteContext(moduleInfo, originalAppContext, gmsContext, null));
|
||||
}
|
||||
moduleInfo.init(dynamiteContext);
|
||||
|
||||
synchronized (sContextCache) {
|
||||
sContextCache.put(cacheKey, dynamiteContext);
|
||||
}
|
||||
Log.d(TAG, "Created and cached a new DynamiteContext for cacheKey: " + cacheKey);
|
||||
return dynamiteContext;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ClassLoader createClassLoader(DynamiteModuleInfo moduleInfo, Context gmsContext, Context originalContext) {
|
||||
String cacheKey = moduleInfo.getModuleId() + "-" + originalContext.getPackageName();
|
||||
synchronized (sClassLoaderCache) {
|
||||
ClassLoader cached = sClassLoaderCache.get(cacheKey);
|
||||
if (cached != null) {
|
||||
Log.d(TAG, "Using cached ClassLoader for cacheKey: " + cacheKey + " cached: " + cached.hashCode());
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
StringBuilder nativeLoaderDirs = new StringBuilder(gmsContext.getApplicationInfo().nativeLibraryDir);
|
||||
if (SDK_INT >= 23 && Process.is64Bit()) {
|
||||
for (String abi : SUPPORTED_64_BIT_ABIS) {
|
||||
nativeLoaderDirs.append(File.pathSeparator).append(gmsContext.getApplicationInfo().sourceDir).append("!/lib/").append(abi);
|
||||
}
|
||||
} else if (SDK_INT >= 21) {
|
||||
for (String abi : SUPPORTED_32_BIT_ABIS) {
|
||||
nativeLoaderDirs.append(File.pathSeparator).append(gmsContext.getApplicationInfo().sourceDir).append("!/lib/").append(abi);
|
||||
}
|
||||
} else {
|
||||
nativeLoaderDirs.append(File.pathSeparator).append(gmsContext.getApplicationInfo().sourceDir).append("!/lib/").append(CPU_ABI);
|
||||
}
|
||||
ClassLoader classLoader = new PathClassLoader(gmsContext.getApplicationInfo().sourceDir, nativeLoaderDirs.toString(), new FilteredClassLoader(originalContext.getClassLoader(), moduleInfo.getMergedClasses(), moduleInfo.getMergedPackages()));
|
||||
synchronized (sClassLoaderCache) {
|
||||
sClassLoaderCache.put(cacheKey, classLoader);
|
||||
}
|
||||
Log.d(TAG, "Created and cached a new ClassLoader for cacheKey: " + cacheKey + " ClassLoader: " + classLoader.hashCode());
|
||||
return classLoader;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.google.android.gms.chimera;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@Keep
|
||||
public class DynamiteModuleInitializer {
|
||||
private static final String TAG = "DynamiteModule";
|
||||
|
||||
public static void initializeModuleV1(Context context) {
|
||||
initializeModuleV2(context, "com.google.android.gms".equals(context.getPackageName()));
|
||||
}
|
||||
|
||||
public static void initializeModuleV2(Context context, boolean withGmsPackage) {
|
||||
Log.d(TAG, "initializeModuleV2 context: " + context + ", withGmsPackage: " + withGmsPackage);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.google.android.gms.chimera.container;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.google.android.gms.chimera.DynamiteContextFactory;
|
||||
|
||||
public class DynamiteContext extends ContextWrapper {
|
||||
private static final String TAG = "DynamiteContext";
|
||||
private DynamiteModuleInfo moduleInfo;
|
||||
private Context originalContext;
|
||||
private Context gmsContext;
|
||||
private DynamiteContext appContext;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public DynamiteContext(DynamiteModuleInfo moduleInfo, Context base, Context gmsContext, DynamiteContext appContext) {
|
||||
super(base);
|
||||
this.moduleInfo = moduleInfo;
|
||||
this.originalContext = base;
|
||||
this.gmsContext = gmsContext;
|
||||
this.appContext = appContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
if (classLoader == null) {
|
||||
classLoader = DynamiteContextFactory.createClassLoader(moduleInfo, gmsContext, originalContext);
|
||||
}
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
return gmsContext.getPackageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationInfo getApplicationInfo() {
|
||||
return gmsContext.getApplicationInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getApplicationContext() {
|
||||
return appContext;
|
||||
}
|
||||
|
||||
@RequiresApi(24)
|
||||
@Override
|
||||
public Context createDeviceProtectedStorageContext() {
|
||||
return new DynamiteContext(moduleInfo, originalContext.createDeviceProtectedStorageContext(), gmsContext.createDeviceProtectedStorageContext(), appContext);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.chimera.container;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.chimera.DynamiteContextFactory;
|
||||
import com.google.android.gms.dynamic.IObjectWrapper;
|
||||
import com.google.android.gms.dynamic.ObjectWrapper;
|
||||
import com.google.android.gms.dynamite.IDynamiteLoader;
|
||||
|
||||
public class DynamiteLoaderImpl extends IDynamiteLoader.Stub {
|
||||
private static final String TAG = "GmsDynamiteLoaderImpl";
|
||||
|
||||
@Override
|
||||
public IObjectWrapper createModuleContext(IObjectWrapper wrappedContext, String moduleId, int minVersion) throws RemoteException {
|
||||
// We don't have crash utils, so just forward
|
||||
return createModuleContextV2(wrappedContext, moduleId, minVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IObjectWrapper createModuleContextV2(IObjectWrapper wrappedContext, String moduleId, int minVersion) throws RemoteException {
|
||||
Log.d(TAG, "createModuleContext for " + moduleId + " at version " + minVersion);
|
||||
final Context originalContext = (Context) ObjectWrapper.unwrap(wrappedContext);
|
||||
return ObjectWrapper.wrap(DynamiteContextFactory.createDynamiteContext(moduleId, originalContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IObjectWrapper createModuleContextV3(IObjectWrapper wrappedContext, String moduleId, int minVersion, IObjectWrapper wrappedCursor) throws RemoteException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIDynamiteLoaderVersion() throws RemoteException {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModuleVersion(IObjectWrapper wrappedContext, String moduleId) throws RemoteException {
|
||||
return getModuleVersion2(wrappedContext, moduleId, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModuleVersion2(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) throws RemoteException {
|
||||
// We don't have crash utils, so just forward
|
||||
return getModuleVersionV2(wrappedContext, moduleId, updateConfigIfRequired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModuleVersionV2(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) throws RemoteException {
|
||||
final Context context = (Context) ObjectWrapper.unwrap(wrappedContext);
|
||||
if (context == null) {
|
||||
Log.w(TAG, "Invalid client context");
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Class.forName("com.google.android.gms.dynamite.descriptors." + moduleId + ".ModuleDescriptor").getDeclaredField("MODULE_VERSION").getInt(null);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "No such module known: " + moduleId);
|
||||
}
|
||||
|
||||
if (moduleId.equals("com.google.android.gms.firebase_database")) {
|
||||
Log.d(TAG, "returning temp fix module version for " + moduleId + ". Firebase Database will not be functional!");
|
||||
return com.google.android.gms.dynamite.descriptors.com.google.android.gms.firebase_database.ModuleDescriptor.MODULE_VERSION;
|
||||
}
|
||||
if (moduleId.equals("com.google.android.gms.googlecertificates")) {
|
||||
return com.google.android.gms.dynamite.descriptors.com.google.android.gms.googlecertificates.ModuleDescriptor.MODULE_VERSION;
|
||||
}
|
||||
if (moduleId.equals("com.google.android.gms.cast.framework.dynamite")) {
|
||||
Log.d(TAG, "returning temp fix module version for " + moduleId + ". Cast API wil not be functional!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (moduleId.equals("com.google.android.gms.maps_dynamite")) {
|
||||
Log.d(TAG, "returning v1 for maps");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Log.d(TAG, "unimplemented Method: getModuleVersion for " + moduleId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IObjectWrapper getModuleVersionV3(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired, long requestStartTime) throws RemoteException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.google.android.gms.chimera.container;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class DynamiteModuleInfo {
|
||||
private Class<?> descriptor;
|
||||
private String moduleId;
|
||||
|
||||
public DynamiteModuleInfo(String moduleId) {
|
||||
this.moduleId = moduleId;
|
||||
try {
|
||||
this.descriptor = Class.forName("com.google.android.gms.dynamite.descriptors." + moduleId + ".ModuleDescriptor");
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
public String getModuleId() {
|
||||
return moduleId;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
try {
|
||||
return descriptor.getDeclaredField("MODULE_VERSION").getInt(null);
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<String> getMergedPackages() {
|
||||
try {
|
||||
return (Collection<String>) descriptor.getDeclaredField("MERGED_PACKAGES").get(null);
|
||||
} catch (Exception e) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<String> getMergedClasses() {
|
||||
try {
|
||||
return (Collection<String>) descriptor.getDeclaredField("MERGED_CLASSES").get(null);
|
||||
} catch (Exception e) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
public void init(Context dynamiteContext) {
|
||||
try {
|
||||
descriptor.getMethod("init", Context.class).invoke(null, dynamiteContext);
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.google.android.gms.chimera.container;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class FilteredClassLoader extends ClassLoader {
|
||||
private static ClassLoader rootClassLoader;
|
||||
private final Collection<String> allowedClasses;
|
||||
private final Collection<String> allowedPackages;
|
||||
|
||||
static {
|
||||
rootClassLoader = ClassLoader.getSystemClassLoader();
|
||||
if (rootClassLoader == null) {
|
||||
rootClassLoader = FilteredClassLoader.class.getClassLoader();
|
||||
while (rootClassLoader.getParent() != null) {
|
||||
rootClassLoader = rootClassLoader.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FilteredClassLoader(ClassLoader parent, Collection<String> allowedClasses, Collection<String> allowedPackages) {
|
||||
super(parent);
|
||||
this.allowedClasses = new HashSet<>(allowedClasses);
|
||||
this.allowedPackages = new HashSet<>(allowedPackages);
|
||||
}
|
||||
|
||||
private String getPackageName(String name) {
|
||||
int lastIndex = name.lastIndexOf(".");
|
||||
if (lastIndex <= 0) return "";
|
||||
return name.substring(0, lastIndex);
|
||||
}
|
||||
|
||||
private String getClassName(String name) {
|
||||
int lastIndex = name.indexOf("$");
|
||||
if (lastIndex <= 0) return name;
|
||||
return name.substring(0, lastIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if (name.startsWith("java.")) return rootClassLoader.loadClass(name);
|
||||
if (allowedClasses.contains(name) || allowedClasses.contains(getClassName(name)))
|
||||
return super.loadClass(name, resolve);
|
||||
if (allowedClasses.contains("!" + name) || allowedClasses.contains("!" + getClassName(name)))
|
||||
return rootClassLoader.loadClass(name);
|
||||
if (allowedPackages.contains(getPackageName(name))) return super.loadClass(name, resolve);
|
||||
return rootClassLoader.loadClass(name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.common.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.android.gms.common.internal.ISignInButtonCreator;
|
||||
import com.google.android.gms.dynamic.IObjectWrapper;
|
||||
import com.google.android.gms.dynamic.ObjectWrapper;
|
||||
|
||||
public class SignInButtonCreatorImpl extends ISignInButtonCreator.Stub {
|
||||
@Override
|
||||
public IObjectWrapper createSignInButton(IObjectWrapper contextWrapper, int size, int color) {
|
||||
Context context = (Context) ObjectWrapper.unwrap(contextWrapper);
|
||||
// TODO: real view :)
|
||||
return ObjectWrapper.wrap(new View(context));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.dynamite.descriptors.com.google.android.gms.firebase_database;
|
||||
|
||||
public class ModuleDescriptor {
|
||||
public static final String MODULE_ID = "com.google.android.gms.firebase_database";
|
||||
public static final int MODULE_VERSION = 3;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.dynamite.descriptors.com.google.android.gms.googlecertificates;
|
||||
|
||||
public class ModuleDescriptor {
|
||||
public static final String MODULE_ID = "com.google.android.gms.googlecertificates";
|
||||
public static final int MODULE_VERSION = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.dynamite.descriptors.com.google.android.gms.measurement.dynamite;
|
||||
|
||||
public class ModuleDescriptor {
|
||||
public static final String MODULE_ID = "com.google.android.gms.measurement.dynamite";
|
||||
public static final int MODULE_VERSION = 140;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.gcm.http;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.google.android.gms.http.IGoogleHttpService;
|
||||
|
||||
public class GoogleHttpService extends Service {
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new IGoogleHttpService.Stub() {
|
||||
@Override
|
||||
public Bundle checkUrl(String url) throws RemoteException {
|
||||
return null; // allow
|
||||
}
|
||||
}.asBinder();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.plus.plusone;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
import com.google.android.gms.dynamic.IObjectWrapper;
|
||||
import com.google.android.gms.dynamic.ObjectWrapper;
|
||||
import com.google.android.gms.plus.internal.IPlusOneButtonCreator;
|
||||
|
||||
import org.microg.gms.auth.AuthConstants;
|
||||
import org.microg.gms.plus.PlusOneButtonImpl;
|
||||
|
||||
public class PlusOneButtonCreatorImpl extends IPlusOneButtonCreator.Stub {
|
||||
@Override
|
||||
public IObjectWrapper create(IObjectWrapper context, int size, int annotation, String url, int activityRequestCode) throws RemoteException {
|
||||
Context ctx = (Context) ObjectWrapper.unwrap(context);
|
||||
return ObjectWrapper.wrap(new PlusOneButtonImpl(ctx, size, annotation, url, AuthConstants.DEFAULT_ACCOUNT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IObjectWrapper createForAccount(IObjectWrapper context, int size, int annotation, String url, String account) throws RemoteException {
|
||||
Context ctx = (Context) ObjectWrapper.unwrap(context);
|
||||
return ObjectWrapper.wrap(new PlusOneButtonImpl(ctx, size, annotation, url, account));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.recovery;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
public class RecoveryService extends Service {
|
||||
private static final String TAG = "GmsRecoverySvc";
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
Log.d(TAG, "onBind: " + intent);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.google.android.gms.semanticlocation
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.util.Log
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.Feature
|
||||
import com.google.android.gms.common.api.internal.IStatusCallback
|
||||
import com.google.android.gms.common.internal.ConnectionInfo
|
||||
import com.google.android.gms.common.internal.GetServiceRequest
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks
|
||||
import com.google.android.gms.semanticlocation.internal.ISemanticLocationService
|
||||
import com.google.android.gms.semanticlocation.internal.SemanticLocationParameters
|
||||
import org.microg.gms.BaseService
|
||||
import org.microg.gms.common.GmsService
|
||||
|
||||
private const val TAG = "SemanticLocationService"
|
||||
|
||||
private val FEATURES = arrayOf(
|
||||
Feature("semanticlocation_events", 1L),
|
||||
)
|
||||
|
||||
class SemanticLocationService : BaseService(TAG, GmsService.SEMANTIC_LOCATION) {
|
||||
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
|
||||
val connectionInfo = ConnectionInfo().apply {
|
||||
features = FEATURES
|
||||
}
|
||||
callback.onPostInitCompleteWithConnectionInfo(
|
||||
ConnectionResult.SUCCESS,
|
||||
SemanticLocationServiceImpl().asBinder(),
|
||||
connectionInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SemanticLocationServiceImpl : ISemanticLocationService.Stub() {
|
||||
override fun registerSemanticLocationEvents(
|
||||
params: SemanticLocationParameters,
|
||||
callback: IStatusCallback,
|
||||
request: SemanticLocationEventRequest,
|
||||
pendingIntent: PendingIntent
|
||||
) {
|
||||
Log.d(TAG, "registerSemanticLocationEvents: $params")
|
||||
}
|
||||
|
||||
override fun setIncognitoMode(params: SemanticLocationParameters, callback: IStatusCallback, mode: Boolean) {
|
||||
Log.d(TAG, "setIncognitoMode: $params")
|
||||
}
|
||||
|
||||
override fun unregisterSemanticLocationEvents(params: SemanticLocationParameters, callback: IStatusCallback, pendingIntent: PendingIntent) {
|
||||
Log.d(TAG, "unregisterSemanticLocationEvents: $params")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wallet.dynamite;
|
||||
|
||||
public class WalletDynamiteCreatorImpl {
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.firebase.database.connection.idl;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.dynamic.IObjectWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IPersistentConnectionImpl extends IPersistentConnection.Stub {
|
||||
private static final String TAG = "GmsFirebaseDbConImpl";
|
||||
|
||||
@Override
|
||||
public void setup(ConnectionConfig var1, IConnectionAuthTokenProvider var2, IObjectWrapper var3, IPersistentConnectionDelegate var4) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: setup");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: initialize");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: shutdown");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAuthToken() throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: refreshAuthToken");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listen(List<String> var1, IObjectWrapper var2, IListenHashProvider var3, long var4, IRequestResultCallback var6) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: listen");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlisten(List<String> var1, IObjectWrapper var2) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: unlisten");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void purgeOutstandingWrites() throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: purgeOutstandingWrites");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(List<String> var1, IObjectWrapper var2, IRequestResultCallback var3) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: put");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compareAndPut(List<String> var1, IObjectWrapper var2, String var3, IRequestResultCallback var4) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: compareAndPut");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(List<String> var1, IObjectWrapper var2, IRequestResultCallback var3) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: merge");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnectPut(List<String> var1, IObjectWrapper var2, IRequestResultCallback var3) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: onDisconnectPut");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnectMerge(List<String> var1, IObjectWrapper var2, IRequestResultCallback var3) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: onDisconnectMerge");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnectCancel(List<String> var1, IRequestResultCallback var2) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: onDisconnectCancel");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt(String var1) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: interrupt");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(String var1) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: resume");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInterrupted(String var1) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: isInterrupted");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.ads;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.internal.GetServiceRequest;
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||
|
||||
import org.microg.gms.BaseService;
|
||||
import org.microg.gms.common.GmsService;
|
||||
|
||||
public class GService extends BaseService {
|
||||
|
||||
public GService() {
|
||||
super("GmsAdsGSvc", GmsService.GSERVICES, GmsService.ADREQUEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||
callback.onPostInitComplete(ConnectionResult.API_DISABLED, null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth;
|
||||
|
||||
import static android.accounts.AccountManager.VISIBILITY_VISIBLE;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.microg.gms.common.GooglePackagePermission;
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.microg.gms.auth.AuthConstants.DEFAULT_ACCOUNT_TYPE;
|
||||
import static org.microg.gms.auth.AuthConstants.PROVIDER_EXTRA_ACCOUNTS;
|
||||
import static org.microg.gms.auth.AuthConstants.PROVIDER_EXTRA_CLEAR_PASSWORD;
|
||||
import static org.microg.gms.auth.AuthConstants.PROVIDER_METHOD_CLEAR_PASSWORD;
|
||||
import static org.microg.gms.auth.AuthConstants.PROVIDER_METHOD_GET_ACCOUNTS;
|
||||
|
||||
public class AccountContentProvider extends ContentProvider {
|
||||
private static final String TAG = "GmsAuthProvider";
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Bundle call(String method, String arg, Bundle extras) {
|
||||
String suggestedPackageName = null;
|
||||
if (SDK_INT > 19) {
|
||||
suggestedPackageName = getCallingPackage();
|
||||
}
|
||||
String packageName = PackageUtils.getAndCheckCallingPackage(getContext(), suggestedPackageName);
|
||||
boolean hasGooglePackagePermission = PackageUtils.callerHasGooglePackagePermission(getContext(), GooglePackagePermission.ACCOUNT);
|
||||
if (!hasGooglePackagePermission) {
|
||||
String[] packagesForUid = getContext().getPackageManager().getPackagesForUid(Binder.getCallingUid());
|
||||
if (packagesForUid != null && packagesForUid.length != 0)
|
||||
Log.w(TAG, "Not granting extended access to " + Arrays.toString(packagesForUid)
|
||||
+ ", signature: " + PackageUtils.firstSignatureDigest(getContext(), packagesForUid[0]));
|
||||
if (getContext().checkCallingPermission(Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED)
|
||||
throw new SecurityException("Access denied, missing google package permission or GET_ACCOUNTS");
|
||||
}
|
||||
long identityToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (PROVIDER_METHOD_GET_ACCOUNTS.equals(method)) {
|
||||
Bundle result = new Bundle();
|
||||
Account[] accounts = null;
|
||||
if (arg != null && (arg.equals(DEFAULT_ACCOUNT_TYPE) || arg.startsWith(DEFAULT_ACCOUNT_TYPE + "."))) {
|
||||
AccountManager am = AccountManager.get(getContext());
|
||||
accounts = am.getAccountsByTypeForPackage(arg, packageName);
|
||||
if (SDK_INT >= 26 && accounts != null && arg.equals(DEFAULT_ACCOUNT_TYPE)) {
|
||||
for (Account account : accounts) {
|
||||
if (am.getAccountVisibility(account, packageName) == AccountManager.VISIBILITY_UNDEFINED &&
|
||||
(hasGooglePackagePermission || AuthPrefs.isAuthVisible(getContext()))) {
|
||||
Log.d(TAG, "Make account " + account + " visible to " + packageName);
|
||||
am.setAccountVisibility(account, packageName, VISIBILITY_VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (accounts == null) {
|
||||
accounts = new Account[0];
|
||||
}
|
||||
|
||||
result.putParcelableArray(PROVIDER_EXTRA_ACCOUNTS, accounts);
|
||||
return result;
|
||||
} else if (PROVIDER_METHOD_CLEAR_PASSWORD.equals(method) && hasGooglePackagePermission) {
|
||||
Account a = extras.getParcelable(PROVIDER_EXTRA_CLEAR_PASSWORD);
|
||||
AccountManager.get(getContext()).clearPassword(a);
|
||||
return null;
|
||||
}
|
||||
throw new UnsupportedOperationException(String.format("Unsupported method call %s(%s).", method, arg));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identityToken);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountAuthenticatorActivity;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.gms.R;
|
||||
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
import org.microg.gms.people.PeopleManager;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static android.accounts.AccountManager.KEY_ACCOUNT_NAME;
|
||||
import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE;
|
||||
import static android.accounts.AccountManager.KEY_ANDROID_PACKAGE_NAME;
|
||||
import static android.accounts.AccountManager.KEY_AUTHTOKEN;
|
||||
import static android.accounts.AccountManager.KEY_CALLER_PID;
|
||||
import static android.accounts.AccountManager.KEY_CALLER_UID;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
public class AskPermissionActivity extends AccountAuthenticatorActivity {
|
||||
public static final String EXTRA_FROM_ACCOUNT_MANAGER = "from_account_manager";
|
||||
public static final String EXTRA_CONSENT_DATA = "consent_data";
|
||||
|
||||
private static final String TAG = "GmsAuthAskPermission";
|
||||
private AuthManager authManager;
|
||||
private IntentData data;
|
||||
|
||||
private static class IntentData {
|
||||
private String accountName;
|
||||
private String accountType;
|
||||
private Account account;
|
||||
|
||||
private String packageName;
|
||||
private String service;
|
||||
|
||||
private int callerUid;
|
||||
private int callerPid;
|
||||
|
||||
private ConsentData consentData;
|
||||
private boolean fromAccountManager = false;
|
||||
|
||||
private CharSequence appLabel;
|
||||
private Drawable appIcon;
|
||||
|
||||
private IntentData(Intent intent) {
|
||||
if (intent != null) {
|
||||
accountName = intent.getStringExtra(KEY_ACCOUNT_NAME);
|
||||
accountType = intent.getStringExtra(KEY_ACCOUNT_TYPE);
|
||||
packageName = intent.getStringExtra(KEY_ANDROID_PACKAGE_NAME);
|
||||
service = intent.getStringExtra(KEY_AUTHTOKEN);
|
||||
callerUid = intent.getIntExtra(KEY_CALLER_UID, 0);
|
||||
callerPid = intent.getIntExtra(KEY_CALLER_PID, 0);
|
||||
fromAccountManager = intent.hasExtra(EXTRA_FROM_ACCOUNT_MANAGER);
|
||||
if (intent.hasExtra(EXTRA_CONSENT_DATA)) {
|
||||
try {
|
||||
consentData = ConsentData.ADAPTER.decode(intent.getByteArrayExtra(EXTRA_CONSENT_DATA));
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
if (accountName != null && accountType != null) {
|
||||
account = new Account(accountName, accountType);
|
||||
}
|
||||
}
|
||||
|
||||
private void verify(Context context) throws Exception {
|
||||
if (accountName == null || accountType == null || account == null) throw new IllegalArgumentException("Required account information missing");
|
||||
if (packageName == null || service == null) throw new IllegalArgumentException("Required request information missing");
|
||||
if (callerUid == 0) throw new IllegalArgumentException("Required caller information missing");
|
||||
PackageUtils.getAndCheckPackage(context, packageName, callerUid, callerPid);
|
||||
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
|
||||
appLabel = packageManager.getApplicationLabel(applicationInfo);
|
||||
appIcon = packageManager.getApplicationIcon(applicationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.ask_permission);
|
||||
data = new IntentData(getIntent());
|
||||
try {
|
||||
data.verify(this);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Verification failed", e);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// This makes the dialog take up the full width
|
||||
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
|
||||
lp.copyFrom(getWindow().getAttributes());
|
||||
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
getWindow().setAttributes(lp);
|
||||
|
||||
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.cancel(data.packageName.hashCode());
|
||||
|
||||
authManager = new AuthManager(this, data.accountName, data.packageName, data.service);
|
||||
|
||||
Bitmap profileIcon = PeopleManager.getOwnerAvatarBitmap(this, data.accountName, false);
|
||||
|
||||
// receive profile icon
|
||||
if (profileIcon != null) {
|
||||
((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon);
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
final Bitmap profileIcon1 = PeopleManager.getOwnerAvatarBitmap(AskPermissionActivity.this, data.accountName, true);
|
||||
runOnUiThread(() -> ((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon1));
|
||||
}).start();
|
||||
}
|
||||
|
||||
((ImageView) findViewById(R.id.app_icon)).setImageDrawable(data.appIcon);
|
||||
if (isOAuth()) {
|
||||
((TextView) findViewById(R.id.title)).setText(getString(R.string.ask_scope_permission_title, data.appLabel));
|
||||
} else {
|
||||
((TextView) findViewById(R.id.title)).setText(getString(R.string.ask_service_permission_title, data.appLabel));
|
||||
}
|
||||
findViewById(android.R.id.button1).setOnClickListener(v -> onAllow());
|
||||
findViewById(android.R.id.button2).setOnClickListener(v -> onDeny());
|
||||
((ListView) findViewById(R.id.permissions)).setAdapter(new PermissionAdapter());
|
||||
}
|
||||
|
||||
public void onAllow() {
|
||||
authManager.setPermitted(true);
|
||||
findViewById(android.R.id.button1).setEnabled(false);
|
||||
findViewById(android.R.id.button2).setEnabled(false);
|
||||
findViewById(R.id.progress_bar).setVisibility(VISIBLE);
|
||||
findViewById(R.id.no_progress_bar).setVisibility(GONE);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
AuthResponse response = authManager.requestAuthWithBackgroundResolution(data.fromAccountManager);
|
||||
Bundle result = new Bundle();
|
||||
result.putString(KEY_AUTHTOKEN, response.auth);
|
||||
result.putString(KEY_ACCOUNT_NAME, data.accountName);
|
||||
result.putString(KEY_ACCOUNT_TYPE, data.accountType);
|
||||
result.putString(KEY_ANDROID_PACKAGE_NAME, data.packageName);
|
||||
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
|
||||
setAccountAuthenticatorResult(result);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
finish();
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void onDeny() {
|
||||
authManager.setPermitted(false);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
if (data.packageName != null) {
|
||||
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.cancel(data.packageName.hashCode());
|
||||
}
|
||||
super.finish();
|
||||
}
|
||||
|
||||
private boolean isOAuth() {
|
||||
return data.service.startsWith("oauth2:") || data.service.startsWith("oauth:");
|
||||
}
|
||||
|
||||
private String getScopeLabel(String scope) {
|
||||
if (data.consentData != null) {
|
||||
for (ConsentData.ScopeDetails scopeDetails : data.consentData.scopes) {
|
||||
if (scope.equals(scopeDetails.id)) {
|
||||
return scopeDetails.title;
|
||||
}
|
||||
}
|
||||
}
|
||||
String labelResourceId = "permission_scope_";
|
||||
String escapedScope = scope.replace("/", "_").replace("-", "_");
|
||||
if (scope.startsWith("https://")) {
|
||||
labelResourceId += escapedScope.substring(8);
|
||||
} else {
|
||||
labelResourceId += escapedScope;
|
||||
}
|
||||
int labelResource = getResources().getIdentifier(labelResourceId, "string", getPackageName());
|
||||
if (labelResource != 0) {
|
||||
return getString(labelResource);
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private String getScopeDescription(String scope) {
|
||||
if (data.consentData != null) {
|
||||
for (ConsentData.ScopeDetails scopeDetails : data.consentData.scopes) {
|
||||
if (scope.equals(scopeDetails.id)) {
|
||||
return scopeDetails.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getServiceLabel(String service) {
|
||||
int labelResource = getResources().getIdentifier("permission_service_" + service + "_label", "string", getPackageName());
|
||||
if (labelResource != 0) {
|
||||
return getString(labelResource);
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private class PermissionAdapter extends BaseAdapter {
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (isOAuth()) {
|
||||
return data.service.split(" ").length;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItem(int position) {
|
||||
if (isOAuth()) {
|
||||
String tokens = data.service.split(":", 2)[1];
|
||||
return tokens.split(" ")[position];
|
||||
}
|
||||
return data.service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
String item = getItem(position);
|
||||
String label;
|
||||
String description;
|
||||
if (isOAuth()) {
|
||||
label = getScopeLabel(item);
|
||||
description = getScopeDescription(item);
|
||||
} else {
|
||||
label = getServiceLabel(item);
|
||||
description = null;
|
||||
}
|
||||
View view = convertView;
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(AskPermissionActivity.this)
|
||||
.inflate(R.layout.ask_permission_list_entry, parent, false);
|
||||
}
|
||||
((TextView) view.findViewById(android.R.id.text1)).setText(label);
|
||||
TextView textView = (TextView) view.findViewById(android.R.id.text2);
|
||||
if (description != null && !description.isEmpty()) {
|
||||
textView.setText(Html.fromHtml(description.trim().replace("\n", "<br>")));
|
||||
textView.setVisibility(VISIBLE);
|
||||
} else {
|
||||
textView.setVisibility(GONE);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import org.microg.gms.accountaction.ErrorResolverKt;
|
||||
import org.microg.gms.accountaction.Resolution;
|
||||
import org.microg.gms.common.NotOkayException;
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
import org.microg.gms.settings.SettingsContract;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.microg.gms.auth.AuthPrefs.isTrustGooglePermitted;
|
||||
|
||||
public class AuthManager {
|
||||
|
||||
private static final String TAG = "GmsAuthManager";
|
||||
public static final String PERMISSION_TREE_BASE = "com.google.android.googleapps.permission.GOOGLE_AUTH.";
|
||||
public static final String PREF_AUTH_VISIBLE = SettingsContract.Auth.VISIBLE;
|
||||
public static final int ONE_HOUR_IN_SECONDS = 60 * 60;
|
||||
public Map<Object, Object> dynamicFields = new HashMap<>();
|
||||
private final Context context;
|
||||
private final String accountName;
|
||||
private final String packageName;
|
||||
private final String service;
|
||||
private AccountManager accountManager;
|
||||
private Account account;
|
||||
private String packageSignature;
|
||||
private String accountType;
|
||||
|
||||
|
||||
private int delegationType;
|
||||
private String delegateeUserId;
|
||||
private String oauth2Foreground;
|
||||
private String oauth2Prompt;
|
||||
private String itCaveatTypes;
|
||||
private String tokenRequestOptions;
|
||||
public String includeEmail;
|
||||
public String includeProfile;
|
||||
public boolean isGmsApp;
|
||||
public boolean ignoreStoredPermission = false;
|
||||
public boolean forceRefreshToken = false;
|
||||
|
||||
public AuthManager(Context context, String accountName, String packageName, String service) {
|
||||
this.context = context;
|
||||
this.accountName = accountName;
|
||||
this.packageName = packageName;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public String getAccountType() {
|
||||
if (accountType == null)
|
||||
accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
|
||||
return accountType;
|
||||
}
|
||||
|
||||
public AccountManager getAccountManager() {
|
||||
if (accountManager == null)
|
||||
accountManager = AccountManager.get(context);
|
||||
return accountManager;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
if (account == null)
|
||||
account = new Account(accountName, getAccountType());
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setPackageSignature(String packageSignature) {
|
||||
this.packageSignature = packageSignature;
|
||||
}
|
||||
|
||||
public String getPackageSignature() {
|
||||
if (packageSignature == null)
|
||||
packageSignature = PackageUtils.firstSignatureDigest(context, packageName);
|
||||
return packageSignature;
|
||||
}
|
||||
|
||||
public String buildTokenKey(String service) {
|
||||
Uri.Builder builder = Uri.EMPTY.buildUpon();
|
||||
if (delegationType != 0 && delegateeUserId != null)
|
||||
builder.appendQueryParameter("delegation_type", Integer.toString(delegationType))
|
||||
.appendQueryParameter("delegatee_user_id", delegateeUserId);
|
||||
if (tokenRequestOptions != null) builder.appendQueryParameter("token_request_options", tokenRequestOptions);
|
||||
if (includeEmail != null) builder.appendQueryParameter("include_email", includeEmail);
|
||||
if (includeProfile != null) builder.appendQueryParameter("include_profile", includeEmail);
|
||||
String query = builder.build().getEncodedQuery();
|
||||
return packageName + ":" + getPackageSignature() + ":" + service + (query != null ? ("?" + query) : "");
|
||||
}
|
||||
|
||||
public String buildTokenKey() {
|
||||
return buildTokenKey(service);
|
||||
}
|
||||
|
||||
public String buildPermKey() {
|
||||
return "perm." + buildTokenKey();
|
||||
}
|
||||
|
||||
public void setPermitted(boolean value) {
|
||||
setUserData(buildPermKey(), value ? "1" : "0");
|
||||
if (SDK_INT >= 26 && value && packageName != null) {
|
||||
// Make account persistently visible as we already granted access
|
||||
accountManager.setAccountVisibility(getAccount(), packageName, AccountManager.VISIBILITY_VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPermitted() {
|
||||
if (!service.startsWith("oauth")) {
|
||||
if (context.getPackageManager().checkPermission(PERMISSION_TREE_BASE + service, packageName) == PackageManager.PERMISSION_GRANTED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
String perm = getUserData(buildPermKey());
|
||||
if (!"1".equals(perm)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setExpiry(long expiry) {
|
||||
setUserData(buildExpireKey(), Long.toString(expiry));
|
||||
}
|
||||
|
||||
public String getUserData(String key) {
|
||||
return getAccountManager().getUserData(getAccount(), key);
|
||||
}
|
||||
|
||||
public void setUserData(String key, String value) {
|
||||
getAccountManager().setUserData(getAccount(), key, value);
|
||||
}
|
||||
|
||||
public void setDelegation(int delegationType, String delegateeUserId) {
|
||||
if (delegationType != 0 && delegateeUserId != null) {
|
||||
this.delegationType = delegationType;
|
||||
this.delegateeUserId = delegateeUserId;
|
||||
} else {
|
||||
this.delegationType = 0;
|
||||
this.delegateeUserId = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setOauth2Foreground(String oauth2Foreground) {
|
||||
this.oauth2Foreground = oauth2Foreground;
|
||||
}
|
||||
|
||||
public void setOauth2Prompt(String oauth2Prompt) {
|
||||
this.oauth2Prompt = oauth2Prompt;
|
||||
}
|
||||
|
||||
public void setItCaveatTypes(String itCaveatTypes) {
|
||||
this.itCaveatTypes = itCaveatTypes;
|
||||
}
|
||||
|
||||
public void setTokenRequestOptions(String tokenRequestOptions) {
|
||||
this.tokenRequestOptions = tokenRequestOptions;
|
||||
}
|
||||
|
||||
public void putDynamicFiled(Object key, Object value) {
|
||||
this.dynamicFields.put(key, value);
|
||||
}
|
||||
|
||||
public boolean accountExists() {
|
||||
for (Account refAccount : getAccountManager().getAccountsByType(accountType)) {
|
||||
if (refAccount.name.equalsIgnoreCase(accountName)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String peekAuthToken() {
|
||||
Log.d(TAG, "peekAuthToken: " + buildTokenKey());
|
||||
return getAccountManager().peekAuthToken(getAccount(), buildTokenKey());
|
||||
}
|
||||
|
||||
public String getAuthToken() {
|
||||
if (service.startsWith("weblogin:")) return null;
|
||||
if (System.currentTimeMillis() / 1000L >= getExpiry() - 300L) {
|
||||
Log.d(TAG, "token present, but expired");
|
||||
return null;
|
||||
}
|
||||
return peekAuthToken();
|
||||
}
|
||||
|
||||
public String buildExpireKey() {
|
||||
return "EXP." + buildTokenKey();
|
||||
}
|
||||
|
||||
public long getExpiry() {
|
||||
String exp = getUserData(buildExpireKey());
|
||||
if (exp == null) return -1;
|
||||
return Long.parseLong(exp);
|
||||
}
|
||||
|
||||
public void setAuthToken(String auth) {
|
||||
setAuthToken(service, auth);
|
||||
}
|
||||
|
||||
public void setAuthToken(String service, String auth) {
|
||||
getAccountManager().setAuthToken(getAccount(), buildTokenKey(service), auth);
|
||||
if (SDK_INT >= 26 && packageName != null && auth != null) {
|
||||
// Make account persistently visible as we already granted access
|
||||
accountManager.setAccountVisibility(getAccount(), packageName, AccountManager.VISIBILITY_VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateAuthToken() {
|
||||
String authToken = peekAuthToken();
|
||||
invalidateAuthToken(authToken);
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
public void invalidateAuthToken(String auth) {
|
||||
getAccountManager().invalidateAuthToken(accountType, auth);
|
||||
}
|
||||
|
||||
public void storeResponse(AuthResponse response) {
|
||||
if (service.startsWith("weblogin:")) return;
|
||||
if (response.accountId != null)
|
||||
setUserData("GoogleUserId", response.accountId);
|
||||
if (response.Sid != null)
|
||||
setAuthToken("SID", response.Sid);
|
||||
if (response.LSid != null)
|
||||
setAuthToken("LSID", response.LSid);
|
||||
if (response.auth != null && (response.expiry != 0 || response.storeConsentRemotely)) {
|
||||
setAuthToken(response.auth);
|
||||
if (response.expiry > 0) {
|
||||
setExpiry(response.expiry);
|
||||
} else {
|
||||
setExpiry(System.currentTimeMillis() / 1000 + ONE_HOUR_IN_SECONDS); // make valid for one hour by default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSystemApp() {
|
||||
try {
|
||||
int flags = context.getPackageManager().getApplicationInfo(packageName, 0).flags;
|
||||
return (flags & FLAG_SYSTEM) > 0 || (flags & FLAG_UPDATED_SYSTEM_APP) > 0;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AuthResponse requestAuthWithBackgroundResolution(boolean legacy) throws IOException {
|
||||
try {
|
||||
return requestAuth(legacy);
|
||||
} catch (NotOkayException e) {
|
||||
if (e.getMessage() != null) {
|
||||
Resolution errorResolution = ErrorResolverKt.resolveAuthErrorMessage(context, e.getMessage());
|
||||
if (errorResolution != null) {
|
||||
AuthResponse response = ErrorResolverKt.initiateFromBackgroundBlocking(
|
||||
errorResolution,
|
||||
context,
|
||||
getAccount(),
|
||||
// infinite loop is prevented
|
||||
() -> requestAuth(legacy)
|
||||
);
|
||||
if (response == null) throw new IOException(e);
|
||||
return response;
|
||||
} else {
|
||||
throw new IOException(e);
|
||||
}
|
||||
} else {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AuthResponse requestAuthWithForegroundResolution(boolean legacy) throws IOException {
|
||||
try {
|
||||
return requestAuth(legacy);
|
||||
} catch (NotOkayException e) {
|
||||
if (e.getMessage() != null) {
|
||||
Resolution errorResolution = ErrorResolverKt.resolveAuthErrorMessage(context, e.getMessage());
|
||||
if (errorResolution != null) {
|
||||
AuthResponse response = ErrorResolverKt.initiateFromForegroundBlocking(
|
||||
errorResolution,
|
||||
context,
|
||||
getAccount(),
|
||||
// infinite loop is prevented
|
||||
() -> requestAuth(legacy)
|
||||
);
|
||||
if (response == null) throw new IOException(e);
|
||||
return response;
|
||||
} else {
|
||||
throw new IOException(e);
|
||||
}
|
||||
} else {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AuthResponse requestAuth(boolean legacy) throws IOException {
|
||||
if (service.equals(AuthConstants.SCOPE_GET_ACCOUNT_ID)) {
|
||||
AuthResponse response = new AuthResponse();
|
||||
response.accountId = response.auth = getAccountManager().getUserData(getAccount(), "GoogleUserId");
|
||||
return response;
|
||||
}
|
||||
if (isPermitted() || isTrustGooglePermitted(context)) {
|
||||
String token = getAuthToken();
|
||||
if (token != null && !forceRefreshToken) {
|
||||
AuthResponse response = new AuthResponse();
|
||||
response.issueAdvice = "stored";
|
||||
response.auth = token;
|
||||
if (service.startsWith("oauth2:")) {
|
||||
response.grantedScopes = service.substring(7);
|
||||
}
|
||||
response.expiry = getExpiry();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
AuthRequest request = new AuthRequest().fromContext(context)
|
||||
.source("android")
|
||||
.app(packageName, getPackageSignature())
|
||||
.email(accountName)
|
||||
.token(getAccountManager().getPassword(getAccount()))
|
||||
.service(service)
|
||||
.delegation(delegationType, delegateeUserId)
|
||||
.oauth2Foreground(oauth2Foreground)
|
||||
.oauth2Prompt(oauth2Prompt)
|
||||
.oauth2IncludeProfile(includeProfile)
|
||||
.oauth2IncludeEmail(includeEmail)
|
||||
.itCaveatTypes(itCaveatTypes)
|
||||
.tokenRequestOptions(tokenRequestOptions)
|
||||
.systemPartition(isSystemApp())
|
||||
.hasPermission(!ignoreStoredPermission && isPermitted())
|
||||
.putDynamicFiledMap(dynamicFields);
|
||||
if (isGmsApp) {
|
||||
request.appIsGms();
|
||||
}
|
||||
if (legacy) {
|
||||
request.callerIsGms().calledFromAccountManager();
|
||||
} else {
|
||||
request.callerIsApp();
|
||||
}
|
||||
AuthResponse response = request.getResponse();
|
||||
if (!isPermitted() && !isTrustGooglePermitted(context)) {
|
||||
response.auth = null;
|
||||
} else {
|
||||
storeResponse(response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public String getService() {
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.PendingIntentCompat;
|
||||
|
||||
import com.google.android.auth.IAuthManagerService;
|
||||
import com.google.android.gms.R;
|
||||
import com.google.android.gms.auth.AccountChangeEventsRequest;
|
||||
import com.google.android.gms.auth.AccountChangeEventsResponse;
|
||||
import com.google.android.gms.auth.GetHubTokenInternalResponse;
|
||||
import com.google.android.gms.auth.GetHubTokenRequest;
|
||||
import com.google.android.gms.auth.HasCapabilitiesRequest;
|
||||
import com.google.android.gms.auth.TokenData;
|
||||
import com.google.android.gms.common.api.Scope;
|
||||
|
||||
import org.microg.gms.common.GooglePackagePermission;
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static android.accounts.AccountManager.*;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.microg.gms.auth.AskPermissionActivity.EXTRA_CONSENT_DATA;
|
||||
|
||||
public class AuthManagerServiceImpl extends IAuthManagerService.Stub {
|
||||
private static final String TAG = "GmsAuthManagerSvc";
|
||||
|
||||
public static final String KEY_ACCOUNT_FEATURES = "account_features";
|
||||
public static final String KEY_AUTHORITY = "authority";
|
||||
public static final String KEY_CALLBACK_INTENT = "callback_intent";
|
||||
public static final String KEY_CALLER_UID = "callerUid";
|
||||
public static final String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
|
||||
public static final String KEY_CLIENT_PACKAGE_NAME = "clientPackageName";
|
||||
public static final String KEY_HANDLE_NOTIFICATION = "handle_notification";
|
||||
public static final String KEY_REQUEST_ACTIONS = "request_visible_actions";
|
||||
public static final String KEY_REQUEST_VISIBLE_ACTIVITIES = "request_visible_actions";
|
||||
public static final String KEY_SUPPRESS_PROGRESS_SCREEN = "suppressProgressScreen";
|
||||
public static final String KEY_SYNC_EXTRAS = "sync_extras";
|
||||
public static final String KEY_DELEGATION_TYPE = "delegation_type";
|
||||
public static final String KEY_DELEGATEE_USER_ID = "delegatee_user_id";
|
||||
|
||||
public static final String KEY_ERROR = "Error";
|
||||
public static final String KEY_USER_RECOVERY_INTENT = "userRecoveryIntent";
|
||||
|
||||
private final Context context;
|
||||
|
||||
public AuthManagerServiceImpl(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getToken(String accountName, String scope, Bundle extras) {
|
||||
return getTokenWithAccount(new Account(accountName, AuthConstants.DEFAULT_ACCOUNT_TYPE), scope, extras);
|
||||
}
|
||||
|
||||
private List<Scope> getScopes(String scope) {
|
||||
if (!scope.startsWith("oauth2:")) return null;
|
||||
String[] strings = scope.substring(7).split(" ");
|
||||
List<Scope> res = new ArrayList<Scope>();
|
||||
for (String string : strings) {
|
||||
res.add(new Scope(string));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static CharSequence getPackageLabel(String packageName, PackageManager pm) {
|
||||
try {
|
||||
return pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return packageName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountChangeEventsResponse getChangeEvents(AccountChangeEventsRequest request) {
|
||||
return new AccountChangeEventsResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getTokenWithAccount(Account account, String scope, Bundle extras) {
|
||||
String packageName = extras.getString(KEY_ANDROID_PACKAGE_NAME);
|
||||
if (packageName == null || packageName.isEmpty())
|
||||
packageName = extras.getString(KEY_CLIENT_PACKAGE_NAME);
|
||||
packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0));
|
||||
boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false);
|
||||
|
||||
scope = Objects.equals(AuthConstants.SCOPE_OAUTH2, scope) ? AuthConstants.SCOPE_EM_OP_PRO : scope;
|
||||
|
||||
if (!AuthConstants.SCOPE_GET_ACCOUNT_ID.equals(scope))
|
||||
Log.d(TAG, "getToken: account:" + account.name + " scope:" + scope + " extras:" + extras + ", notify: " + notify);
|
||||
|
||||
scope = Objects.equals(AuthConstants.SCOPE_OAUTH2, scope) ? AuthConstants.SCOPE_EM_OP_PRO : scope;
|
||||
|
||||
/*
|
||||
* TODO: This scope seems to be invalid (according to https://developers.google.com/oauthplayground/),
|
||||
* but is used in some applications anyway. Removing it is unlikely a good solution, but works for now.
|
||||
*/
|
||||
scope = scope.replace("https://www.googleapis.com/auth/identity.plus.page.impersonation ", "");
|
||||
|
||||
AuthManager authManager = new AuthManager(context, account.name, packageName, scope);
|
||||
if (extras.containsKey(KEY_DELEGATION_TYPE) && extras.getInt(KEY_DELEGATION_TYPE) != 0 ) {
|
||||
authManager.setDelegation(extras.getInt(KEY_DELEGATION_TYPE), extras.getString("delegatee_user_id"));
|
||||
}
|
||||
authManager.setOauth2Foreground(notify ? "0" : "1");
|
||||
Bundle result = new Bundle();
|
||||
result.putString(KEY_ACCOUNT_NAME, account.name);
|
||||
result.putString(KEY_ACCOUNT_TYPE, authManager.getAccountType());
|
||||
if (!authManager.accountExists()) {
|
||||
result.putString(KEY_ERROR, "NetworkError");
|
||||
return result;
|
||||
}
|
||||
try {
|
||||
AuthResponse res = authManager.requestAuthWithBackgroundResolution(false);
|
||||
if (res.auth != null) {
|
||||
if (!AuthConstants.SCOPE_GET_ACCOUNT_ID.equals(scope))
|
||||
Log.d(TAG, "getToken: " + res);
|
||||
result.putString(KEY_AUTHTOKEN, res.auth);
|
||||
Bundle details = new Bundle();
|
||||
details.putParcelable("TokenData", new TokenData(res.auth, res.expiry, scope.startsWith("oauth2:"), getScopes(res.grantedScopes != null ? res.grantedScopes : scope)));
|
||||
result.putBundle("tokenDetails", details);
|
||||
result.putString(KEY_ERROR, "OK");
|
||||
} else {
|
||||
result.putString(KEY_ERROR, "NeedPermission");
|
||||
Intent i = new Intent(context, AskPermissionActivity.class);
|
||||
i.putExtras(extras);
|
||||
i.putExtra(KEY_ANDROID_PACKAGE_NAME, packageName);
|
||||
i.putExtra(KEY_ACCOUNT_TYPE, authManager.getAccountType());
|
||||
i.putExtra(KEY_ACCOUNT_NAME, account.name);
|
||||
i.putExtra(KEY_AUTHTOKEN, scope);
|
||||
i.putExtra(KEY_CALLER_UID, getCallingUid());
|
||||
i.putExtra(KEY_CALLER_PID, getCallingPid());
|
||||
try {
|
||||
if (res.consentDataBase64 != null)
|
||||
i.putExtra(EXTRA_CONSENT_DATA, Base64.decode(res.consentDataBase64, Base64.URL_SAFE));
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Can't decode consent data: ", e);
|
||||
}
|
||||
if (notify) {
|
||||
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.notify(packageName.hashCode(), new NotificationCompat.Builder(context)
|
||||
.setContentIntent(PendingIntentCompat.getActivity(context, 0, i, 0, false))
|
||||
.setContentTitle(context.getString(R.string.auth_notification_title))
|
||||
.setContentText(context.getString(R.string.auth_notification_content, getPackageLabel(packageName, context.getPackageManager())))
|
||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
.build());
|
||||
}
|
||||
result.putParcelable(KEY_USER_RECOVERY_INTENT, i);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
result.putString(KEY_ERROR, "NetworkError");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAccounts(Bundle extras) {
|
||||
PackageUtils.assertGooglePackagePermission(context, GooglePackagePermission.ACCOUNT);
|
||||
String[] accountFeatures = extras.getStringArray(KEY_ACCOUNT_FEATURES);
|
||||
String accountType = extras.getString(KEY_ACCOUNT_TYPE);
|
||||
Account[] accounts;
|
||||
if (accountFeatures != null) {
|
||||
try {
|
||||
accounts = AccountManager.get(context).getAccountsByTypeAndFeatures(accountType, accountFeatures, null, null).getResult(5, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
accounts = AccountManager.get(context).getAccountsByType(accountType);
|
||||
}
|
||||
Bundle res = new Bundle();
|
||||
res.putParcelableArray(KEY_ACCOUNTS, accounts);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle removeAccount(Account account) {
|
||||
Log.w(TAG, "Not implemented: removeAccount(" + account + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle requestGoogleAccountsAccess(String packageName) throws RemoteException {
|
||||
PackageUtils.assertGooglePackagePermission(context, GooglePackagePermission.ACCOUNT);
|
||||
if (SDK_INT >= 26) {
|
||||
Map<Account, Integer> visibilityForPackage = get(context).getAccountsAndVisibilityForPackage(packageName, AuthConstants.DEFAULT_ACCOUNT_TYPE);
|
||||
for (Account account : get(context).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE)) {
|
||||
Integer visibility = visibilityForPackage.get(account);
|
||||
if (visibility != null && visibility != VISIBILITY_VISIBLE) {
|
||||
get(context).setAccountVisibility(account, packageName, VISIBILITY_VISIBLE);
|
||||
}
|
||||
}
|
||||
Bundle res = new Bundle();
|
||||
res.putString("Error", "Ok");
|
||||
return res;
|
||||
} else {
|
||||
Log.w(TAG, "Not implemented: requestGoogleAccountsAccess(" + packageName + ")");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hasCapabilities(HasCapabilitiesRequest request) throws RemoteException {
|
||||
PackageUtils.assertGooglePackagePermission(context, GooglePackagePermission.ACCOUNT);
|
||||
List<String> services = Arrays.asList(AccountManager.get(context).getUserData(request.account, "services").split(","));
|
||||
for (String capability : request.capabilities) {
|
||||
if (capability.startsWith("service_") && !services.contains(capability.substring(8)) || !services.contains(capability)) {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "Not fully implemented: hasCapabilities(" + request.account + ", " + Arrays.toString(request.capabilities) + ")");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GetHubTokenInternalResponse getHubToken(GetHubTokenRequest request, Bundle extras) throws RemoteException {
|
||||
Log.w(TAG, "Not implemented: getHubToken()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("MissingPermission") // Workaround bug in Android Linter
|
||||
public Bundle clearToken(String token, Bundle extras) {
|
||||
String packageName = extras.getString(KEY_ANDROID_PACKAGE_NAME);
|
||||
if (packageName == null) packageName = extras.getString(KEY_CLIENT_PACKAGE_NAME);
|
||||
packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0));
|
||||
|
||||
Log.d(TAG, "clearToken: token:" + token + " extras:" + extras);
|
||||
AccountManager.get(context).invalidateAuthToken(AuthConstants.DEFAULT_ACCOUNT_TYPE, token);
|
||||
|
||||
Bundle res = new Bundle();
|
||||
res.putString("Error", "Ok");
|
||||
res.putBoolean("booleanResult", true);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
||||
if (super.onTransact(code, data, reply, flags)) return true;
|
||||
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.login;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.google.android.gms.R;
|
||||
|
||||
public abstract class AssistantActivity extends AppCompatActivity {
|
||||
private static final int TITLE_MIN_HEIGHT = 64;
|
||||
private static final double TITLE_WIDTH_FACTOR = (8.0 / 18.0);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.login_assistant);
|
||||
formatTitle();
|
||||
findViewById(R.id.next_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onNextButtonClicked();
|
||||
}
|
||||
});
|
||||
findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onBackButtonClicked();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("WrongViewCast")
|
||||
private void formatTitle() {
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
double widthPixels = (double) (getResources().getDisplayMetrics().widthPixels);
|
||||
findViewById(R.id.title_container).getLayoutParams().height =
|
||||
(int) (dpToPx(TITLE_MIN_HEIGHT) + (TITLE_WIDTH_FACTOR * widthPixels));
|
||||
} else {
|
||||
findViewById(R.id.title_container).getLayoutParams().height = dpToPx(TITLE_MIN_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNextButtonText(@StringRes int res) {
|
||||
setNextButtonText(getText(res));
|
||||
}
|
||||
|
||||
public void setNextButtonText(CharSequence text) {
|
||||
if (text == null) {
|
||||
findViewById(R.id.next_button).setVisibility(View.GONE);
|
||||
} else {
|
||||
findViewById(R.id.next_button).setVisibility(View.VISIBLE);
|
||||
((Button) findViewById(R.id.next_button)).setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBackButtonText(@StringRes int res) {
|
||||
setBackButtonText(getText(res));
|
||||
}
|
||||
|
||||
public void setBackButtonText(CharSequence text) {
|
||||
if (text == null) {
|
||||
findViewById(R.id.back_button).setVisibility(View.GONE);
|
||||
} else {
|
||||
findViewById(R.id.back_button).setVisibility(View.VISIBLE);
|
||||
((Button) findViewById(R.id.back_button)).setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onNextButtonClicked() {
|
||||
|
||||
}
|
||||
|
||||
protected void onBackButtonClicked() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
formatTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTitleChanged(CharSequence title, int color) {
|
||||
super.onTitleChanged(title, color);
|
||||
((TextView) findViewById(R.id.title)).setText(title);
|
||||
}
|
||||
|
||||
public int dpToPx(int dp) {
|
||||
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
|
||||
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,727 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.login;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.webkit.WebViewClientCompat;
|
||||
|
||||
import com.google.android.gms.R;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.microg.gms.accountaction.AccountNotificationKt;
|
||||
import org.microg.gms.auth.AuthConstants;
|
||||
import org.microg.gms.auth.AuthManager;
|
||||
import org.microg.gms.auth.AuthRequest;
|
||||
import org.microg.gms.auth.AuthResponse;
|
||||
import org.microg.gms.checkin.CheckinManager;
|
||||
import org.microg.gms.checkin.LastCheckinInfo;
|
||||
import org.microg.gms.common.Constants;
|
||||
import org.microg.gms.common.HttpFormClient;
|
||||
import org.microg.gms.common.Utils;
|
||||
import org.microg.gms.people.PeopleManager;
|
||||
import org.microg.gms.profile.Build;
|
||||
import org.microg.gms.profile.ProfileManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Locale;
|
||||
|
||||
import static android.accounts.AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE;
|
||||
import static android.accounts.AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.telephony.TelephonyManager.SIM_STATE_UNKNOWN;
|
||||
import static android.view.KeyEvent.KEYCODE_BACK;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||
import static org.microg.gms.auth.AuthPrefs.isAuthVisible;
|
||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||
import static org.microg.gms.common.Constants.GMS_VERSION_CODE;
|
||||
import static org.microg.gms.common.Constants.VENDING_PACKAGE_NAME;
|
||||
import static org.microg.gms.gcm.ExtensionsKt.ACTION_GCM_REGISTER_ACCOUNT;
|
||||
import static org.microg.gms.gcm.ExtensionsKt.KEY_GCM_REGISTER_ACCOUNT_NAME;
|
||||
|
||||
public class LoginActivity extends AssistantActivity {
|
||||
public static final String TMPL_NEW_ACCOUNT = "new_account";
|
||||
public static final String EXTRA_TMPL = "tmpl";
|
||||
public static final String EXTRA_EMAIL = "email";
|
||||
public static final String EXTRA_TOKEN = "masterToken";
|
||||
public static final String EXTRA_RE_AUTH_ACCOUNT = "re_auth_account";
|
||||
public static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
|
||||
|
||||
private static final String TAG = "GmsAuthLoginBrowser";
|
||||
private static final String EMBEDDED_SETUP_URL = "https://accounts.google.com/EmbeddedSetup";
|
||||
private static final String EMBEDDED_RE_AUTH_URL = "https://accounts.google.com/embedded/reauth/v2/android";
|
||||
private static final String PROGRAMMATIC_AUTH_URL = "https://accounts.google.com/o/oauth2/programmatic_auth";
|
||||
private static final String GOOGLE_SUITE_URL = "https://accounts.google.com/signin/continue";
|
||||
private static final String MAGIC_USER_AGENT = " MinuteMaid";
|
||||
private static final String COOKIE_OAUTH_TOKEN = "oauth_token";
|
||||
private static final String ACTION_UPDATE_ACCOUNT = "com.google.android.gms.auth.GOOGLE_ACCOUNT_CHANGE";
|
||||
private static final String PERMISSION_UPDATE_ACCOUNT = "com.google.android.gms.auth.permission.GOOGLE_ACCOUNT_CHANGE";
|
||||
|
||||
private final FidoHandler fidoHandler = new FidoHandler(this);
|
||||
private final DroidGuardHandler dgHandler = new DroidGuardHandler(this);
|
||||
|
||||
private WebView webView;
|
||||
private String accountType;
|
||||
private AccountManager accountManager;
|
||||
private AccountAuthenticatorResponse response;
|
||||
private InputMethodManager inputMethodManager;
|
||||
private ViewGroup authContent;
|
||||
private int state = 0;
|
||||
private boolean isReAuth = false;
|
||||
private Account reAuthAccount;
|
||||
|
||||
@SuppressLint("AddJavascriptInterface")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
|
||||
accountManager = AccountManager.get(LoginActivity.this);
|
||||
inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
webView = createWebView(this);
|
||||
webView.addJavascriptInterface(new JsBridge(), "mm");
|
||||
authContent = (ViewGroup) findViewById(R.id.auth_content);
|
||||
((ViewGroup) findViewById(R.id.auth_root)).addView(webView);
|
||||
webView.setWebViewClient(new WebViewClientCompat() {
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
Log.d(TAG, "pageFinished: " + view.getUrl());
|
||||
Uri uri = Uri.parse(view.getUrl());
|
||||
|
||||
// Begin login.
|
||||
// Only required if client code does not invoke showView() via JSBridge
|
||||
if ("identifier".equals(uri.getFragment()) || uri.getPath().endsWith("/identifier"))
|
||||
runOnUiThread(() -> webView.setVisibility(VISIBLE));
|
||||
|
||||
// Normal login.
|
||||
if ("close".equals(uri.getFragment()))
|
||||
closeWeb(false);
|
||||
|
||||
// Google Suite login.
|
||||
if (url.startsWith(GOOGLE_SUITE_URL))
|
||||
closeWeb(false);
|
||||
|
||||
// IDK when this is called.
|
||||
if (url.startsWith(PROGRAMMATIC_AUTH_URL))
|
||||
closeWeb(true);
|
||||
}
|
||||
});
|
||||
if(getIntent().hasExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE)){
|
||||
Object tempObject = getIntent().getExtras().get(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
|
||||
if (tempObject instanceof AccountAuthenticatorResponse) {
|
||||
response = (AccountAuthenticatorResponse) tempObject;
|
||||
}
|
||||
}
|
||||
if (getIntent().hasExtra(EXTRA_RE_AUTH_ACCOUNT)) {
|
||||
reAuthAccount = getIntent().getParcelableExtra(EXTRA_RE_AUTH_ACCOUNT);
|
||||
isReAuth = reAuthAccount != null;
|
||||
}
|
||||
if (getIntent().hasExtra(EXTRA_TOKEN)) {
|
||||
if (getIntent().hasExtra(EXTRA_EMAIL)) {
|
||||
AccountManager accountManager = AccountManager.get(this);
|
||||
Account account = new Account(getIntent().getStringExtra(EXTRA_EMAIL), accountType);
|
||||
accountManager.addAccountExplicitly(account, getIntent().getStringExtra(EXTRA_TOKEN), null);
|
||||
if (isAuthVisible(this) && SDK_INT >= 26) {
|
||||
accountManager.setAccountVisibility(account, PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE, VISIBILITY_USER_MANAGED_VISIBLE);
|
||||
}
|
||||
retrieveGmsToken(account);
|
||||
} else {
|
||||
retrieveRtToken(getIntent().getStringExtra(EXTRA_TOKEN));
|
||||
}
|
||||
} else if (android.os.Build.VERSION.SDK_INT < 21 || isReAuth) {
|
||||
init();
|
||||
} else {
|
||||
setMessage(R.string.auth_before_connect);
|
||||
setBackButtonText(android.R.string.cancel);
|
||||
setNextButtonText(R.string.auth_sign_in);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNextButtonClicked() {
|
||||
super.onNextButtonClicked();
|
||||
state++;
|
||||
if (state == 1) {
|
||||
init();
|
||||
} else if (state == -1) {
|
||||
loginCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBackButtonClicked() {
|
||||
super.onBackButtonClicked();
|
||||
state--;
|
||||
if (state == -1) {
|
||||
loginCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
public void loginCanceled() {
|
||||
Log.d(TAG, "loginCanceled: ");
|
||||
setResult(RESULT_CANCELED);
|
||||
if (response != null) {
|
||||
response.onError(AccountManager.ERROR_CODE_CANCELED, "Canceled");
|
||||
}
|
||||
if (SDK_INT >= 21) { finishAndRemoveTask(); } else finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
loginCanceled();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setTitle(R.string.just_a_sec);
|
||||
setBackButtonText(null);
|
||||
setNextButtonText(null);
|
||||
View loading = getLayoutInflater().inflate(R.layout.login_assistant_loading, authContent, false);
|
||||
authContent.removeAllViews();
|
||||
authContent.addView(loading);
|
||||
setMessage(R.string.auth_connecting);
|
||||
CookieManager.getInstance().setAcceptCookie(true);
|
||||
if (SDK_INT >= 21) {
|
||||
CookieManager.getInstance().removeAllCookies(value -> start());
|
||||
} else {
|
||||
//noinspection deprecation
|
||||
CookieManager.getInstance().removeAllCookie();
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
private static WebView createWebView(Context context) {
|
||||
WebView webView = new WebView(context);
|
||||
if (SDK_INT < 21) {
|
||||
webView.setVisibility(VISIBLE);
|
||||
} else {
|
||||
webView.setVisibility(INVISIBLE);
|
||||
}
|
||||
webView.setLayoutParams(new RelativeLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
webView.setBackgroundColor(Color.TRANSPARENT);
|
||||
prepareWebViewSettings(context, webView.getSettings());
|
||||
return webView;
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
private static void prepareWebViewSettings(Context context, WebSettings settings) {
|
||||
ProfileManager.ensureInitialized(context);
|
||||
settings.setUserAgentString(Build.INSTANCE.generateWebViewUserAgentString(settings.getUserAgentString()) + MAGIC_USER_AGENT);
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setSupportMultipleWindows(false);
|
||||
settings.setSaveFormData(false);
|
||||
settings.setAllowFileAccess(false);
|
||||
settings.setDatabaseEnabled(false);
|
||||
settings.setNeedInitialFocus(false);
|
||||
settings.setUseWideViewPort(false);
|
||||
settings.setSupportZoom(false);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(false);
|
||||
}
|
||||
|
||||
private void start() {
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
|
||||
if (networkInfo != null && networkInfo.isConnected()) {
|
||||
if (LastCheckinInfo.read(this).getAndroidId() == 0) {
|
||||
new Thread(() -> {
|
||||
Runnable next;
|
||||
next = checkin(false) ? this::loadLoginPage : () -> showError(R.string.auth_general_error_desc);
|
||||
LoginActivity.this.runOnUiThread(next);
|
||||
}).start();
|
||||
} else {
|
||||
loadLoginPage();
|
||||
}
|
||||
} else {
|
||||
showError(R.string.no_network_error_desc);
|
||||
}
|
||||
}
|
||||
|
||||
private void showError(int errorRes) {
|
||||
setTitle(R.string.sorry);
|
||||
findViewById(R.id.progress_bar).setVisibility(View.INVISIBLE);
|
||||
setMessage(errorRes);
|
||||
}
|
||||
|
||||
private void setMessage(@StringRes int res) {
|
||||
setMessage(getText(res));
|
||||
}
|
||||
|
||||
private void setMessage(CharSequence text) {
|
||||
((TextView) findViewById(R.id.description_text)).setText(text);
|
||||
}
|
||||
|
||||
private void loadLoginPage() {
|
||||
String tmpl = getIntent().hasExtra(EXTRA_TMPL) ? getIntent().getStringExtra(EXTRA_TMPL) : TMPL_NEW_ACCOUNT;
|
||||
webView.loadUrl(buildUrl(tmpl, Utils.getLocale(this)));
|
||||
}
|
||||
|
||||
protected void runScript(String js) {
|
||||
runOnUiThread(() -> webView.loadUrl("javascript:" + js));
|
||||
}
|
||||
|
||||
private void closeWeb(boolean programmaticAuth) {
|
||||
setMessage(R.string.auth_finalize);
|
||||
runOnUiThread(() -> webView.setVisibility(INVISIBLE));
|
||||
String cookies = CookieManager.getInstance().getCookie(programmaticAuth ? PROGRAMMATIC_AUTH_URL : EMBEDDED_SETUP_URL);
|
||||
String[] temp = cookies.split(";");
|
||||
for (String ar1 : temp) {
|
||||
if (ar1.trim().startsWith(COOKIE_OAUTH_TOKEN + "=")) {
|
||||
String[] temp1 = ar1.split("=");
|
||||
retrieveRtToken(temp1[1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
showError(R.string.auth_general_error_desc);
|
||||
}
|
||||
|
||||
private void retrieveRtToken(String oAuthToken) {
|
||||
new AuthRequest().fromContext(this)
|
||||
.appIsGms()
|
||||
.callerIsGms()
|
||||
.service("ac2dm")
|
||||
.token(oAuthToken).isAccessToken()
|
||||
.addAccount()
|
||||
.getAccountId()
|
||||
.droidguardResults(null /*TODO*/)
|
||||
.getResponseAsync(new HttpFormClient.Callback<AuthResponse>() {
|
||||
@Override
|
||||
public void onResponse(AuthResponse response) {
|
||||
Account account = new Account(response.email, accountType);
|
||||
if (isReAuth && reAuthAccount != null && reAuthAccount.name.equals(account.name)) {
|
||||
accountManager.removeAccount(account, future -> saveAccount(account, response), null);
|
||||
} else {
|
||||
saveAccount(account, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception exception) {
|
||||
Log.w(TAG, "onException", exception);
|
||||
runOnUiThread(() -> {
|
||||
showError(R.string.auth_general_error_desc);
|
||||
setNextButtonText(android.R.string.ok);
|
||||
});
|
||||
state = -2;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveAccount(Account account, AuthResponse response) {
|
||||
if (accountManager.addAccountExplicitly(account, response.token, null)) {
|
||||
accountManager.setAuthToken(account, "SID", response.Sid);
|
||||
accountManager.setAuthToken(account, "LSID", response.LSid);
|
||||
accountManager.setUserData(account, "flags", "1");
|
||||
accountManager.setUserData(account, "services", response.services);
|
||||
accountManager.setUserData(account, "oauthAccessToken", "1");
|
||||
accountManager.setUserData(account, "firstName", response.firstName);
|
||||
accountManager.setUserData(account, "lastName", response.lastName);
|
||||
if (!TextUtils.isEmpty(response.accountId))
|
||||
accountManager.setUserData(account, "GoogleUserId", response.accountId);
|
||||
|
||||
retrieveGmsToken(account);
|
||||
setResult(RESULT_OK);
|
||||
} else {
|
||||
Log.w(TAG, "Account NOT created!");
|
||||
runOnUiThread(() -> {
|
||||
showError(R.string.auth_general_error_desc);
|
||||
setNextButtonText(android.R.string.ok);
|
||||
});
|
||||
state = -2;
|
||||
}
|
||||
}
|
||||
|
||||
private void returnSuccessResponse(Account account){
|
||||
if (isReAuth && reAuthAccount != null) {
|
||||
AccountNotificationKt.cancelAccountNotificationChannel(this, reAuthAccount);
|
||||
}
|
||||
if(response != null){
|
||||
Bundle bd = new Bundle();
|
||||
bd.putString(AccountManager.KEY_ACCOUNT_NAME,account.name);
|
||||
bd.putBoolean("new_account_created",false);
|
||||
bd.putString(AccountManager.KEY_ACCOUNT_TYPE,accountType);
|
||||
response.onResult(bd);
|
||||
}
|
||||
Intent intent = new Intent(ACTION_UPDATE_ACCOUNT);
|
||||
intent.setPackage(VENDING_PACKAGE_NAME);
|
||||
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
|
||||
sendBroadcast(intent, PERMISSION_UPDATE_ACCOUNT);
|
||||
}
|
||||
private void retrieveGmsToken(final Account account) {
|
||||
final AuthManager authManager = new AuthManager(this, account.name, GMS_PACKAGE_NAME, "ac2dm");
|
||||
authManager.setPermitted(true);
|
||||
new AuthRequest().fromContext(this)
|
||||
.appIsGms()
|
||||
.callerIsGms()
|
||||
.service(authManager.getService())
|
||||
.email(account.name)
|
||||
.token(AccountManager.get(this).getPassword(account))
|
||||
.systemPartition(true)
|
||||
.hasPermission(true)
|
||||
.addAccount()
|
||||
.getAccountId()
|
||||
.getResponseAsync(new HttpFormClient.Callback<AuthResponse>() {
|
||||
@Override
|
||||
public void onResponse(AuthResponse response) {
|
||||
authManager.storeResponse(response);
|
||||
String accountId = PeopleManager.loadUserInfo(LoginActivity.this, account);
|
||||
if (!TextUtils.isEmpty(accountId))
|
||||
accountManager.setUserData(account, "GoogleUserId", accountId);
|
||||
if (isAuthVisible(LoginActivity.this) && SDK_INT >= 26) {
|
||||
accountManager.setAccountVisibility(account, PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE, VISIBILITY_USER_MANAGED_VISIBLE);
|
||||
}
|
||||
checkin(true);
|
||||
returnSuccessResponse(account);
|
||||
notifyGcmGroupUpdate(account.name);
|
||||
if (SDK_INT >= 21) { finishAndRemoveTask(); } else finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception exception) {
|
||||
Log.w(TAG, "onException", exception);
|
||||
runOnUiThread(() -> {
|
||||
showError(R.string.auth_general_error_desc);
|
||||
setNextButtonText(android.R.string.ok);
|
||||
});
|
||||
state = -2;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void notifyGcmGroupUpdate(String accountName) {
|
||||
Intent intent = new Intent(ACTION_GCM_REGISTER_ACCOUNT);
|
||||
intent.setPackage(Constants.GMS_PACKAGE_NAME);
|
||||
intent.putExtra(KEY_GCM_REGISTER_ACCOUNT_NAME, accountName);
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private boolean checkin(boolean force) {
|
||||
try {
|
||||
CheckinManager.checkin(LoginActivity.this, force);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Checkin failed", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if ((keyCode == KEYCODE_BACK) && webView.canGoBack() && (webView.getVisibility() == VISIBLE)) {
|
||||
webView.goBack();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private String buildUrl(String tmpl, Locale locale) {
|
||||
String uriString = isReAuth ? EMBEDDED_RE_AUTH_URL : EMBEDDED_SETUP_URL;
|
||||
Uri.Builder builder = Uri.parse(uriString).buildUpon()
|
||||
.appendQueryParameter("source", "android")
|
||||
.appendQueryParameter("xoauth_display_name", "Android Device")
|
||||
.appendQueryParameter("lang", locale.getLanguage())
|
||||
.appendQueryParameter("cc", locale.getCountry().toLowerCase(Locale.US))
|
||||
.appendQueryParameter("langCountry", locale.toString().toLowerCase(Locale.US))
|
||||
.appendQueryParameter("hl", locale.toString().replace("_", "-"))
|
||||
.appendQueryParameter("tmpl", tmpl);
|
||||
if (isReAuth && reAuthAccount != null) {
|
||||
builder.appendQueryParameter("Email", reAuthAccount.name);
|
||||
}
|
||||
return builder.build().toString();
|
||||
}
|
||||
|
||||
private class JsBridge {
|
||||
|
||||
@JavascriptInterface
|
||||
public final void addAccount(String json) {
|
||||
Log.d(TAG, "JSBridge: addAccount");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void attemptLogin(String accountName, String password) {
|
||||
Log.d(TAG, "JSBridge: attemptLogin");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void backupSyncOptIn(String accountName) {
|
||||
Log.d(TAG, "JSBridge: backupSyncOptIn");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void cancelFido2SignRequest() {
|
||||
Log.d(TAG, "JSBridge: cancelFido2SignRequest");
|
||||
fidoHandler.cancel();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void clearOldLoginAttempts() {
|
||||
Log.d(TAG, "JSBridge: clearOldLoginAttempts");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void closeView() {
|
||||
Log.d(TAG, "JSBridge: closeView");
|
||||
closeWeb(false);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void fetchIIDToken(String entity) {
|
||||
Log.d(TAG, "JSBridge: fetchIIDToken");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final String fetchVerifiedPhoneNumber() {
|
||||
Log.d(TAG, "JSBridge: fetchVerifiedPhoneNumber");
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("MissingPermission")
|
||||
@JavascriptInterface
|
||||
public final String getAccounts() {
|
||||
Log.d(TAG, "JSBridge: getAccounts");
|
||||
Account[] accountsByType = accountManager.getAccountsByType(accountType);
|
||||
JSONArray json = new JSONArray();
|
||||
for (Account account : accountsByType) {
|
||||
json.put(account.name);
|
||||
}
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final String getAllowedDomains() {
|
||||
Log.d(TAG, "JSBridge: getAllowedDomains");
|
||||
return new JSONArray().toString();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final String getAndroidId() {
|
||||
long androidId = LastCheckinInfo.read(LoginActivity.this).getAndroidId();
|
||||
Log.d(TAG, "JSBridge: getAndroidId");
|
||||
if (androidId == 0 || androidId == -1) return null;
|
||||
return Long.toHexString(androidId);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final int getAuthModuleVersionCode() {
|
||||
return GMS_VERSION_CODE;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final int getBuildVersionSdk() {
|
||||
return Build.VERSION.SDK_INT;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public int getDeviceContactsCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final int getDeviceDataVersionInfo() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void getDroidGuardResult(String s) {
|
||||
Log.d(TAG, "JSBridge: getDroidGuardResult");
|
||||
try {
|
||||
JSONArray array = new JSONArray(s);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getAndroidId()).append(":").append(getBuildVersionSdk()).append(":").append(getPlayServicesVersionCode());
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
sb.append(":").append(array.getString(i));
|
||||
}
|
||||
String dg = Base64.encodeToString(MessageDigest.getInstance("SHA1").digest(sb.toString().getBytes()), 0);
|
||||
dgHandler.start(dg);
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final String getFactoryResetChallenges() {
|
||||
return new JSONArray().toString();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final String getPhoneNumber() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final int getPlayServicesVersionCode() {
|
||||
return GMS_VERSION_CODE;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final String getSimSerial() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final int getSimState() {
|
||||
return SIM_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void goBack() {
|
||||
Log.d(TAG, "JSBridge: goBack");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final boolean hasPhoneNumber() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final boolean hasTelephony() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void hideKeyboard() {
|
||||
inputMethodManager.hideSoftInputFromWindow(webView.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final boolean isUserOwner() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void launchEmergencyDialer() {
|
||||
Log.d(TAG, "JSBridge: launchEmergencyDialer");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void log(String s) {
|
||||
Log.d(TAG, "JSBridge: log");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void notifyOnTermsOfServiceAccepted() {
|
||||
Log.d(TAG, "JSBridge: notifyOnTermsOfServiceAccepted");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void sendFido2SkUiEvent(String event) {
|
||||
Log.d(TAG, "JSBridge: sendFido2SkUiEvent");
|
||||
fidoHandler.onEvent(event);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void setAccountIdentifier(String accountName) {
|
||||
Log.d(TAG, "JSBridge: setAccountIdentifier");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setAllActionsEnabled(boolean z) {
|
||||
Log.d(TAG, "JSBridge: setAllActionsEnabled");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void setBackButtonEnabled(boolean backButtonEnabled) {
|
||||
int visibility = getWindow().getDecorView().getSystemUiVisibility();
|
||||
if (backButtonEnabled)
|
||||
visibility &= -STATUS_BAR_DISABLE_BACK;
|
||||
else
|
||||
visibility |= STATUS_BAR_DISABLE_BACK;
|
||||
getWindow().getDecorView().setSystemUiVisibility(visibility);
|
||||
}
|
||||
|
||||
|
||||
@JavascriptInterface
|
||||
public final void setNewAccountCreated() {
|
||||
Log.d(TAG, "JSBridge: setNewAccountCreated");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setPrimaryActionEnabled(boolean z) {
|
||||
Log.d(TAG, "JSBridge: setPrimaryActionEnabled");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setPrimaryActionLabel(String str, int i) {
|
||||
Log.d(TAG, "JSBridge: setPrimaryActionLabel: " + str);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setSecondaryActionEnabled(boolean z) {
|
||||
Log.d(TAG, "JSBridge: setSecondaryActionEnabled");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setSecondaryActionLabel(String str, int i) {
|
||||
Log.d(TAG, "JSBridge: setSecondaryActionLabel: " + str);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void showKeyboard() {
|
||||
inputMethodManager.showSoftInput(webView, SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void showView() {
|
||||
runOnUiThread(() -> webView.setVisibility(VISIBLE));
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void skipLogin() {
|
||||
Log.d(TAG, "JSBridge: skipLogin");
|
||||
loginCanceled();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void startAfw() {
|
||||
Log.d(TAG, "JSBridge: startAfw");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public final void startFido2SignRequest(String request) {
|
||||
Log.d(TAG, "JSBridge: startFido2SignRequest");
|
||||
fidoHandler.startSignRequest(request);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.loginservice;
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.NetworkErrorException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.internal.CertData;
|
||||
import org.microg.gms.auth.*;
|
||||
import org.microg.gms.auth.login.LoginActivity;
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
import org.microg.gms.auth.AuthResponse;
|
||||
import org.microg.gms.utils.PackageManagerUtilsKt;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static android.accounts.AccountManager.*;
|
||||
|
||||
public class AccountAuthenticator extends AbstractAccountAuthenticator {
|
||||
private static final String TAG = "GmsAuthenticator";
|
||||
public static final String KEY_OVERRIDE_PACKAGE = "overridePackage";
|
||||
public static final String KEY_OVERRIDE_CERTIFICATE = "overrideCertificate";
|
||||
private final Context context;
|
||||
private final String accountType;
|
||||
|
||||
public AccountAuthenticator(Context context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
this.accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
|
||||
Log.d(TAG, "editProperties: " + accountType);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
|
||||
if (accountType.equals(this.accountType)) {
|
||||
final Intent i = new Intent(context, LoginActivity.class);
|
||||
i.putExtras(options);
|
||||
i.putExtra(LoginActivity.EXTRA_TMPL, LoginActivity.TMPL_NEW_ACCOUNT);
|
||||
i.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||
final Bundle result = new Bundle();
|
||||
result.putParcelable(KEY_INTENT, i);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
|
||||
Log.d(TAG, "confirmCredentials: " + account + ", " + options);
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isPackageOverrideAllowed(Account account, String requestingPackage, String overridePackage, CertData overrideCertificate) {
|
||||
// Always allow for self package
|
||||
if (requestingPackage.equals(context.getPackageName())) return true;
|
||||
// if (requestingPackage.equals("org.microg.example.authwithoverride")) return true;
|
||||
String requestingDigestString = PackageManagerUtilsKt.toHexString(PackageManagerUtilsKt.digest(PackageManagerUtilsKt.getCertificates(context.getPackageManager(), requestingPackage).get(0), "SHA-256"), "");
|
||||
String overrideCertificateDigestString = PackageManagerUtilsKt.toHexString(PackageManagerUtilsKt.digest(overrideCertificate, "SHA-256"), "");
|
||||
String overrideUserDataKey = "override." + requestingPackage + ":" + requestingDigestString + ":" + overridePackage + ":" + overrideCertificateDigestString;
|
||||
String hasOverride = AccountManager.get(context).getUserData(account, overrideUserDataKey);
|
||||
return "1".equals(hasOverride);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
|
||||
options.keySet();
|
||||
Log.d(TAG, "getAuthToken: " + account + ", " + authTokenType + ", " + options);
|
||||
String app = options.getString(KEY_ANDROID_PACKAGE_NAME);
|
||||
app = PackageUtils.getAndCheckPackage(context, app, options.getInt(KEY_CALLER_UID), options.getInt(KEY_CALLER_PID));
|
||||
AuthManager authManager;
|
||||
if (app == null) {
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(KEY_ERROR_CODE, ERROR_CODE_BAD_REQUEST);
|
||||
return result;
|
||||
}
|
||||
if (options.containsKey(KEY_OVERRIDE_PACKAGE) || options.containsKey(KEY_OVERRIDE_CERTIFICATE)) {
|
||||
String overridePackage = options.getString(KEY_OVERRIDE_PACKAGE, app);
|
||||
byte[] overrideCertificateBytes = options.getByteArray(KEY_OVERRIDE_CERTIFICATE);
|
||||
CertData overrideCert;
|
||||
if (overrideCertificateBytes != null) {
|
||||
overrideCert = new CertData(overrideCertificateBytes);
|
||||
} else {
|
||||
overrideCert = PackageManagerUtilsKt.getCertificates(context.getPackageManager(), app).get(0);
|
||||
}
|
||||
if (isPackageOverrideAllowed(account, app, overridePackage, overrideCert)) {
|
||||
authManager = new AuthManager(context, account.name, overridePackage, authTokenType);
|
||||
authManager.setPackageSignature(PackageManagerUtilsKt.toHexString(PackageManagerUtilsKt.digest(overrideCert, "SHA1"), ""));
|
||||
} else {
|
||||
Bundle result = new Bundle();
|
||||
Intent i = new Intent(context, AskPackageOverrideActivity.class);
|
||||
i.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||
i.putExtra(KEY_ANDROID_PACKAGE_NAME, app);
|
||||
i.putExtra(KEY_ACCOUNT_TYPE, account.type);
|
||||
i.putExtra(KEY_ACCOUNT_NAME, account.name);
|
||||
i.putExtra(KEY_OVERRIDE_PACKAGE, overridePackage);
|
||||
i.putExtra(KEY_OVERRIDE_CERTIFICATE, overrideCert.getBytes());
|
||||
result.putParcelable(KEY_INTENT, i);
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
authManager = new AuthManager(context, account.name, app, authTokenType);
|
||||
}
|
||||
try {
|
||||
AuthResponse res = authManager.requestAuthWithBackgroundResolution(true);
|
||||
if (res.auth != null) {
|
||||
Log.d(TAG, "getAuthToken: " + res.auth);
|
||||
Bundle result = new Bundle();
|
||||
result.putString(KEY_ACCOUNT_TYPE, account.type);
|
||||
result.putString(KEY_ACCOUNT_NAME, account.name);
|
||||
result.putString(KEY_AUTHTOKEN, res.auth);
|
||||
return result;
|
||||
} else {
|
||||
Bundle result = new Bundle();
|
||||
Intent i = new Intent(context, AskPermissionActivity.class);
|
||||
i.putExtras(options);
|
||||
i.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||
i.putExtra(KEY_ANDROID_PACKAGE_NAME, app);
|
||||
i.putExtra(KEY_ACCOUNT_TYPE, account.type);
|
||||
i.putExtra(KEY_ACCOUNT_NAME, account.name);
|
||||
i.putExtra(KEY_AUTHTOKEN, authTokenType);
|
||||
try {
|
||||
if (res.consentDataBase64 != null)
|
||||
i.putExtra(AskPermissionActivity.EXTRA_CONSENT_DATA, Base64.decode(res.consentDataBase64, Base64.URL_SAFE));
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Can't decode consent data: ", e);
|
||||
}
|
||||
result.putParcelable(KEY_INTENT, i);
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthTokenLabel(String authTokenType) {
|
||||
Log.d(TAG, "getAuthTokenLabel: " + authTokenType);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
|
||||
Log.d(TAG, "updateCredentials: " + account + ", " + authTokenType + ", " + options);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
String services = accountManager.getUserData(account, "services");
|
||||
boolean res = true;
|
||||
if (services != null) {
|
||||
List<String> servicesList = Arrays.asList(services.split(","));
|
||||
for (String feature : features) {
|
||||
if (feature.startsWith("service_") && !servicesList.contains(feature.substring(8))) {
|
||||
Log.d(TAG, "Feature " + feature + " not supported");
|
||||
res = false;
|
||||
} else if (!feature.startsWith("service_") && !servicesList.contains(feature)) {
|
||||
Log.d(TAG, "Feature " + feature + " not supported");
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = false;
|
||||
}
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(KEY_BOOLEAN_RESULT, res);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.loginservice;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import static android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT;
|
||||
|
||||
public class GoogleLoginService extends Service {
|
||||
private AccountAuthenticator authenticator;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
authenticator = new AccountAuthenticator(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
if (intent.getAction().equals(ACTION_AUTHENTICATOR_INTENT)) {
|
||||
return authenticator.getIBinder();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.car;
|
||||
|
||||
import com.google.android.gms.common.internal.GetServiceRequest;
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||
|
||||
import org.microg.gms.BaseService;
|
||||
import org.microg.gms.common.GmsService;
|
||||
|
||||
public class CarService extends BaseService {
|
||||
public CarService() {
|
||||
super("GmsCarSvc", GmsService.CAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.checkin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.microg.gms.common.DeviceConfigProtoKt;
|
||||
import org.microg.gms.common.DeviceConfiguration;
|
||||
import org.microg.gms.common.DeviceIdentifier;
|
||||
import org.microg.gms.common.PhoneInfo;
|
||||
import org.microg.gms.common.Utils;
|
||||
import org.microg.gms.profile.Build;
|
||||
import org.microg.gms.profile.ProfileManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class CheckinClient {
|
||||
private static final String TAG = "GmsCheckinClient";
|
||||
private static final Object TODO = null; // TODO
|
||||
private static final List<String> TODO_LIST_STRING = new ArrayList<>(); // TODO
|
||||
private static final List<CheckinRequest.Checkin.Statistic> TODO_LIST_CHECKIN = new ArrayList<>(); // TODO
|
||||
private static final String SERVICE_URL = "https://android.clients.google.com/checkin";
|
||||
|
||||
public static CheckinResponse request(CheckinRequest request) throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(SERVICE_URL).openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestProperty("Content-type", "application/x-protobuffer");
|
||||
connection.setRequestProperty("Content-Encoding", "gzip");
|
||||
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||
connection.setRequestProperty("User-Agent", "Android-Checkin/2.0 (vbox86p JLS36G); gzip");
|
||||
|
||||
Log.d(TAG, "-- Request --\n" + request);
|
||||
OutputStream os = new GZIPOutputStream(connection.getOutputStream());
|
||||
os.write(request.encode());
|
||||
os.close();
|
||||
|
||||
if (connection.getResponseCode() != 200) {
|
||||
try {
|
||||
throw new IOException(new String(Utils.readStreamToEnd(new GZIPInputStream(connection.getErrorStream()))));
|
||||
} catch (Exception e) {
|
||||
throw new IOException(connection.getResponseMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
InputStream is = connection.getInputStream();
|
||||
CheckinResponse response = CheckinResponse.ADAPTER.decode(new GZIPInputStream(is));
|
||||
is.close();
|
||||
return response;
|
||||
}
|
||||
|
||||
public static CheckinRequest makeRequest(Context context, DeviceConfiguration deviceConfiguration,
|
||||
DeviceIdentifier deviceIdent, PhoneInfo phoneInfo,
|
||||
LastCheckinInfo checkinInfo, Locale locale,
|
||||
List<Account> accounts) {
|
||||
ProfileManager.ensureInitialized(context);
|
||||
CheckinRequest.Builder builder = new CheckinRequest.Builder()
|
||||
.accountCookie(new ArrayList<>())
|
||||
.androidId(checkinInfo.getAndroidId())
|
||||
.checkin(new CheckinRequest.Checkin.Builder()
|
||||
.build(new CheckinRequest.Checkin.Build.Builder()
|
||||
.bootloader(Build.BOOTLOADER)
|
||||
.brand(Build.BRAND)
|
||||
.clientId("android-google")
|
||||
.device(Build.DEVICE)
|
||||
.fingerprint(Build.FINGERPRINT)
|
||||
.hardware(Build.HARDWARE)
|
||||
.manufacturer(Build.MANUFACTURER)
|
||||
.model(Build.MODEL)
|
||||
.otaInstalled(false) // TODO?
|
||||
//.packageVersionCode(Constants.MAX_REFERENCE_VERSION)
|
||||
.product(Build.PRODUCT)
|
||||
.radio(Build.RADIO)
|
||||
.sdkVersion(Build.VERSION.SDK_INT)
|
||||
.time(Build.TIME / 1000)
|
||||
.build())
|
||||
.cellOperator(phoneInfo.cellOperator)
|
||||
.event(Collections.singletonList(new CheckinRequest.Checkin.Event.Builder()
|
||||
.tag(checkinInfo.getAndroidId() == 0 ? "event_log_start" : "system_update")
|
||||
.value_(checkinInfo.getAndroidId() == 0 ? null : "1536,0,-1,NULL")
|
||||
.timeMs(new Date().getTime())
|
||||
.build()))
|
||||
.lastCheckinMs(checkinInfo.getLastCheckin())
|
||||
.requestedGroup(TODO_LIST_STRING)
|
||||
.roaming(phoneInfo.roaming)
|
||||
.simOperator(phoneInfo.simOperator)
|
||||
.stat(TODO_LIST_CHECKIN)
|
||||
.userNumber(0)
|
||||
.build())
|
||||
.deviceConfiguration(DeviceConfigProtoKt.asProto(deviceConfiguration))
|
||||
.digest(checkinInfo.getDigest())
|
||||
.esn(deviceIdent.esn)
|
||||
.fragment(0)
|
||||
.locale(locale.toString())
|
||||
.loggingId(new Random().nextLong()) // TODO: static
|
||||
.meid(deviceIdent.meid)
|
||||
.otaCert(Collections.singletonList("71Q6Rn2DDZl1zPDVaaeEHItd"))
|
||||
.serial(Build.SERIAL != null && !Build.SERIAL.isEmpty() ? Build.SERIAL : null)
|
||||
.timeZone(TimeZone.getDefault().getID())
|
||||
.userName((String) TODO)
|
||||
.userSerialNumber((Integer) TODO)
|
||||
.version(3);
|
||||
for (Account account : accounts) {
|
||||
builder.accountCookie.add("[" + account.name + "]");
|
||||
builder.accountCookie.add(account.authToken);
|
||||
}
|
||||
if (builder.accountCookie.isEmpty()) builder.accountCookie.add("");
|
||||
if (deviceIdent.wifiMac != null) {
|
||||
builder.macAddress(Arrays.asList(deviceIdent.wifiMac))
|
||||
.macAddressType(Arrays.asList("wifi"));
|
||||
}
|
||||
if (checkinInfo.getSecurityToken() != 0) {
|
||||
builder.securityToken(checkinInfo.getSecurityToken())
|
||||
.fragment(1);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static class Account {
|
||||
public final String name;
|
||||
public final String authToken;
|
||||
|
||||
public Account(String accountName, String authToken) {
|
||||
this.name = accountName;
|
||||
this.authToken = authToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.checkin;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
|
||||
import org.microg.gms.auth.AuthConstants;
|
||||
import org.microg.gms.auth.AuthRequest;
|
||||
import org.microg.gms.common.Constants;
|
||||
import org.microg.gms.common.DeviceConfiguration;
|
||||
import org.microg.gms.common.Utils;
|
||||
import org.microg.gms.gservices.GServices;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CheckinManager {
|
||||
private static final long MIN_CHECKIN_INTERVAL = 3 * 60 * 60 * 1000; // 3 hours
|
||||
|
||||
@SuppressWarnings("MissingPermission")
|
||||
public static synchronized LastCheckinInfo checkin(Context context, boolean force) throws IOException {
|
||||
LastCheckinInfo info = LastCheckinInfo.read(context);
|
||||
if (!force && info.getLastCheckin() > System.currentTimeMillis() - MIN_CHECKIN_INTERVAL)
|
||||
return null;
|
||||
if (!CheckinPreferences.isEnabled(context))
|
||||
return null;
|
||||
List<CheckinClient.Account> accounts = new ArrayList<CheckinClient.Account>();
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
String accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
|
||||
for (Account account : accountManager.getAccountsByType(accountType)) {
|
||||
String token = new AuthRequest()
|
||||
.email(account.name).token(accountManager.getPassword(account))
|
||||
.hasPermission(true).service("ac2dm")
|
||||
.app("com.google.android.gsf", Constants.GMS_PACKAGE_SIGNATURE_SHA1)
|
||||
.getResponse().LSid;
|
||||
if (token != null) {
|
||||
accounts.add(new CheckinClient.Account(account.name, token));
|
||||
}
|
||||
}
|
||||
CheckinRequest request = CheckinClient.makeRequest(context,
|
||||
new DeviceConfiguration(context), Utils.getDeviceIdentifier(context),
|
||||
Utils.getPhoneInfo(context), info, Utils.getLocale(context), accounts);
|
||||
return handleResponse(context, CheckinClient.request(request));
|
||||
}
|
||||
|
||||
private static LastCheckinInfo handleResponse(Context context, CheckinResponse response) {
|
||||
LastCheckinInfo info = new LastCheckinInfo(response);
|
||||
info.write(context);
|
||||
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
for (CheckinResponse.GservicesSetting setting : response.setting) {
|
||||
GServices.setString(resolver, setting.name.utf8(), setting.value_.utf8());
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.checkin;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Activity;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.IntentService;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.PendingIntentCompat;
|
||||
import androidx.legacy.content.WakefulBroadcastReceiver;
|
||||
|
||||
import com.google.android.gms.checkin.internal.ICheckinService;
|
||||
|
||||
import org.microg.gms.auth.AuthConstants;
|
||||
import org.microg.gms.common.ForegroundServiceInfo;
|
||||
import org.microg.gms.common.ForegroundServiceContext;
|
||||
import org.microg.gms.gcm.McsService;
|
||||
import org.microg.gms.people.PeopleManager;
|
||||
|
||||
@ForegroundServiceInfo(value = "Google device registration", resName = "service_name_checkin", resPackage = "com.google.android.gms")
|
||||
public class CheckinService extends IntentService {
|
||||
private static final String TAG = "GmsCheckinSvc";
|
||||
public static final long MAX_VALID_CHECKIN_AGE = 24 * 60 * 60 * 1000; // 12 hours
|
||||
public static final long REGULAR_CHECKIN_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours
|
||||
public static final long BACKUP_CHECKIN_DELAY = 3 * 60 * 60 * 1000; // 3 hours
|
||||
public static final String BIND_ACTION = "com.google.android.gms.checkin.BIND_TO_SERVICE";
|
||||
public static final String EXTRA_FORCE_CHECKIN = "force";
|
||||
@Deprecated
|
||||
public static final String EXTRA_CALLBACK_INTENT = "callback";
|
||||
public static final String EXTRA_RESULT_RECEIVER = "receiver";
|
||||
public static final String EXTRA_NEW_CHECKIN_TIME = "checkin_time";
|
||||
|
||||
private ICheckinService iface = new ICheckinService.Stub() {
|
||||
@Override
|
||||
public String getDeviceDataVersionInfo() throws RemoteException {
|
||||
return LastCheckinInfo.read(CheckinService.this).getDeviceDataVersionInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastCheckinSuccessTime() throws RemoteException {
|
||||
return LastCheckinInfo.read(CheckinService.this).getLastCheckin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastSimOperator() throws RemoteException {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public CheckinService() {
|
||||
super(TAG);
|
||||
}
|
||||
|
||||
@SuppressWarnings("MissingPermission")
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
try {
|
||||
ForegroundServiceContext.completeForegroundService(this, intent, TAG);
|
||||
if (CheckinPreferences.isEnabled(this)) {
|
||||
LastCheckinInfo info = CheckinManager.checkin(this, intent.getBooleanExtra(EXTRA_FORCE_CHECKIN, false));
|
||||
if (info != null) {
|
||||
Log.d(TAG, "Checked in as " + Long.toHexString(info.getAndroidId()));
|
||||
String accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
|
||||
for (Account account : AccountManager.get(this).getAccountsByType(accountType)) {
|
||||
PeopleManager.loadUserInfo(this, account);
|
||||
}
|
||||
McsService.scheduleReconnect(this);
|
||||
if (intent.hasExtra(EXTRA_CALLBACK_INTENT)) {
|
||||
startService((Intent) intent.getParcelableExtra(EXTRA_CALLBACK_INTENT));
|
||||
}
|
||||
if (intent.hasExtra(EXTRA_RESULT_RECEIVER)) {
|
||||
ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER);
|
||||
if (receiver != null) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong(EXTRA_NEW_CHECKIN_TIME, info.getLastCheckin());
|
||||
receiver.send(Activity.RESULT_OK, bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
} finally {
|
||||
if (intent != null) {
|
||||
WakefulBroadcastReceiver.completeWakefulIntent(intent);
|
||||
}
|
||||
schedule(this);
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
if (BIND_ACTION.equals(intent.getAction())) {
|
||||
return iface.asBinder();
|
||||
} else {
|
||||
return super.onBind(intent);
|
||||
}
|
||||
}
|
||||
|
||||
static void schedule(Context context) {
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
PendingIntent pendingIntent = PendingIntentCompat.getService(context, TriggerReceiver.class.getName().hashCode(), new Intent(context, TriggerReceiver.class), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT, false);
|
||||
alarmManager.set(AlarmManager.RTC, Math.max(LastCheckinInfo.read(context).getLastCheckin() + REGULAR_CHECKIN_INTERVAL, System.currentTimeMillis() + BACKUP_CHECKIN_DELAY), pendingIntent);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.checkin;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkRequest;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.PendingIntentCompat;
|
||||
import androidx.legacy.content.WakefulBroadcastReceiver;
|
||||
|
||||
import org.microg.gms.common.ForegroundServiceContext;
|
||||
|
||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.microg.gms.checkin.CheckinService.EXTRA_FORCE_CHECKIN;
|
||||
import static org.microg.gms.checkin.CheckinService.REGULAR_CHECKIN_INTERVAL;
|
||||
|
||||
public class TriggerReceiver extends WakefulBroadcastReceiver {
|
||||
private static final String TAG = "GmsCheckinTrigger";
|
||||
private static boolean registered = false;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction());
|
||||
|
||||
if (CheckinPreferences.isEnabled(context) || force) {
|
||||
if (LastCheckinInfo.read(context).getLastCheckin() > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL && !force) {
|
||||
CheckinService.schedule(context);
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
|
||||
if (networkInfo != null && networkInfo.isConnected() || force) {
|
||||
Intent subIntent = new Intent(context, CheckinService.class);
|
||||
subIntent.putExtra(EXTRA_FORCE_CHECKIN, force);
|
||||
startWakefulService(new ForegroundServiceContext(context), subIntent);
|
||||
} else if (SDK_INT >= 23) {
|
||||
// no network, register a network callback to retry when we have internet
|
||||
NetworkRequest networkRequest = new NetworkRequest.Builder()
|
||||
.addCapability(NET_CAPABILITY_INTERNET)
|
||||
.build();
|
||||
Intent i = new Intent(context, TriggerReceiver.class);
|
||||
PendingIntent pendingIntent = PendingIntentCompat.getBroadcast(context, 0, i, FLAG_UPDATE_CURRENT, true);
|
||||
cm.registerNetworkCallback(networkRequest, pendingIntent);
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Ignoring " + intent + ": checkin is disabled");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.drive.api;
|
||||
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.google.android.gms.common.internal.GetServiceRequest;
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||
|
||||
import org.microg.gms.BaseService;
|
||||
import org.microg.gms.common.GmsService;
|
||||
|
||||
public class DriveApiService extends BaseService {
|
||||
private DriveServiceImpl impl = new DriveServiceImpl();
|
||||
|
||||
public DriveApiService() {
|
||||
super("GmsDriveApiSvc", GmsService.DRIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||
callback.onPostInitComplete(0, impl.asBinder(), null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.drive.api;
|
||||
|
||||
import android.content.IntentSender;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.drive.internal.AddEventListenerRequest;
|
||||
import com.google.android.gms.drive.internal.AddPermissionRequest;
|
||||
import com.google.android.gms.drive.internal.AuthorizeAccessRequest;
|
||||
import com.google.android.gms.drive.internal.CancelPendingActionsRequest;
|
||||
import com.google.android.gms.drive.internal.ChangeResourceParentsRequest;
|
||||
import com.google.android.gms.drive.internal.CheckResourceIdsExistRequest;
|
||||
import com.google.android.gms.drive.internal.CloseContentsAndUpdateMetadataRequest;
|
||||
import com.google.android.gms.drive.internal.CloseContentsRequest;
|
||||
import com.google.android.gms.drive.internal.ControlProgressRequest;
|
||||
import com.google.android.gms.drive.internal.CreateContentsRequest;
|
||||
import com.google.android.gms.drive.internal.CreateFileIntentSenderRequest;
|
||||
import com.google.android.gms.drive.internal.CreateFileRequest;
|
||||
import com.google.android.gms.drive.internal.CreateFolderRequest;
|
||||
import com.google.android.gms.drive.internal.DeleteResourceRequest;
|
||||
import com.google.android.gms.drive.internal.DisconnectRequest;
|
||||
import com.google.android.gms.drive.internal.DriveServiceResponse;
|
||||
import com.google.android.gms.drive.internal.FetchThumbnailRequest;
|
||||
import com.google.android.gms.drive.internal.GetChangesRequest;
|
||||
import com.google.android.gms.drive.internal.GetDriveIdFromUniqueIdRequest;
|
||||
import com.google.android.gms.drive.internal.GetMetadataRequest;
|
||||
import com.google.android.gms.drive.internal.GetPermissionsRequest;
|
||||
import com.google.android.gms.drive.internal.IDriveService;
|
||||
import com.google.android.gms.drive.internal.IDriveServiceCallbacks;
|
||||
import com.google.android.gms.drive.internal.IEventCallback;
|
||||
import com.google.android.gms.drive.internal.ListParentsRequest;
|
||||
import com.google.android.gms.drive.internal.LoadRealtimeRequest;
|
||||
import com.google.android.gms.drive.internal.OpenContentsRequest;
|
||||
import com.google.android.gms.drive.internal.OpenFileIntentSenderRequest;
|
||||
import com.google.android.gms.drive.internal.RealtimeDocumentSyncRequest;
|
||||
import com.google.android.gms.drive.internal.RemoveEventListenerRequest;
|
||||
import com.google.android.gms.drive.internal.RemovePermissionRequest;
|
||||
import com.google.android.gms.drive.internal.SetDrivePreferencesRequest;
|
||||
import com.google.android.gms.drive.internal.SetFileUploadPreferencesRequest;
|
||||
import com.google.android.gms.drive.internal.SetResourceParentsRequest;
|
||||
import com.google.android.gms.drive.internal.StreamContentsRequest;
|
||||
import com.google.android.gms.drive.internal.TrashResourceRequest;
|
||||
import com.google.android.gms.drive.internal.UnsubscribeResourceRequest;
|
||||
import com.google.android.gms.drive.internal.UntrashResourceRequest;
|
||||
import com.google.android.gms.drive.internal.UpdateMetadataRequest;
|
||||
import com.google.android.gms.drive.internal.UpdatePermissionRequest;
|
||||
|
||||
public class DriveServiceImpl extends IDriveService.Stub {
|
||||
private static final String TAG = "GmsDriveSvcImpl";
|
||||
|
||||
@Override
|
||||
public void getMetadata(GetMetadataRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getMetadata");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMetadata(UpdateMetadataRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: updateMetadata");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createContents(CreateContentsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: createContents");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createFile(CreateFileRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: createFile");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createFolder(CreateFolderRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: createFolder");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DriveServiceResponse openContents(OpenContentsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: openContents");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeContents(CloseContentsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: closeContents");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestSync(IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: requestSync");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntentSender openFileIntentSender(OpenFileIntentSenderRequest request) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: openFileIntentSender");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntentSender createFileIntentSender(CreateFileIntentSenderRequest request) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: createFileIntentSender");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authorizeAccess(AuthorizeAccessRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: authorizeAccess");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listParents(ListParentsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: listParents");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEventListener(AddEventListenerRequest request, IEventCallback callback, String unused, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: addEventListener");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEventListener(RemoveEventListenerRequest request, IEventCallback callback, String unused, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: removeEventListener");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(DisconnectRequest request) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: disconnect");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trashResource(TrashResourceRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: trashResource");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeContentsAndUpdateMetadata(CloseContentsAndUpdateMetadataRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: closeContentsAndUpdateMetadata");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteResource(DeleteResourceRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: deleteResource");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadRealtime(LoadRealtimeRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: loadRealtime");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceParents(SetResourceParentsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: setResourceParents");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDriveIdFromUniqueId(GetDriveIdFromUniqueIdRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getDriveIdFromUniqueId");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkResourceIdsExist(CheckResourceIdsExistRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: checkResourceIdsExist");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completePendingAction(IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: completePendingAction");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDrivePreferences(IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getDrivePreferences");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrivePreferences(SetDrivePreferencesRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: setDrivePreferences");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void realtimeDocumentSync(RealtimeDocumentSyncRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: realtimeDocumentSync");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceUsagePreferences(IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getDeviceUsagePreferences");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFileUploadPreferences(SetFileUploadPreferencesRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: setFileUploadPreferences");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelPendingActions(CancelPendingActionsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: cancelPendingActions");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void untrashResource(UntrashResourceRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: untrashResource");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isAutoBackupEnabled(IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: isAutoBackupEnabled");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchThumbnail(FetchThumbnailRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: fetchThumbnail");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChanges(GetChangesRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getChanges");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeResource(UnsubscribeResourceRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: unsubscribeResource");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getPermissions(GetPermissionsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getPermissions");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermission(AddPermissionRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: addPermission");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePermission(UpdatePermissionRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: updatePermission");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePermission(RemovePermissionRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: removePermission");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeQueryResultListener(IEventCallback callback, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: removeQueryResultListener");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controlProgress(ControlProgressRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: controlProgress");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeResourceParents(ChangeResourceParentsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: changeResourceParents");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DriveServiceResponse streamContents(StreamContentsRequest request, IDriveServiceCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: streamContents");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.feeds;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
|
||||
public class SubscribedFeedsProvider extends ContentProvider{
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
return new MatrixCursor(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.games;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.core.app.PendingIntentCompat;
|
||||
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||
import com.google.android.gms.common.internal.GetServiceRequest;
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||
|
||||
import org.microg.gms.BaseService;
|
||||
import org.microg.gms.common.GmsService;
|
||||
|
||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||
import static org.microg.gms.games.UpgradeActivity.ACTION_PLAY_GAMES_UPGRADE;
|
||||
import static org.microg.gms.games.UpgradeActivity.EXTRA_GAME_PACACKE_NAME;
|
||||
|
||||
public class GamesStubService extends BaseService {
|
||||
|
||||
public static final String PARAM_GAME_PACKAGE_NAME = "com.google.android.gms.games.key.gamePackageName";
|
||||
|
||||
public GamesStubService() {
|
||||
super("GmsGamesSvc", GmsService.GAMES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||
String packageName = null;
|
||||
if (request.extras != null) {
|
||||
packageName = request.extras.getString(PARAM_GAME_PACKAGE_NAME);
|
||||
}
|
||||
if (packageName == null) packageName = GMS_PACKAGE_NAME;
|
||||
Intent intent = new Intent(ACTION_PLAY_GAMES_UPGRADE);
|
||||
intent.setPackage(GMS_PACKAGE_NAME);
|
||||
intent.putExtra(EXTRA_GAME_PACACKE_NAME, packageName);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable("pendingIntent", PendingIntentCompat.getActivity(this, packageName.hashCode(), intent, FLAG_UPDATE_CURRENT, false));
|
||||
callback.onPostInitComplete(CommonStatusCodes.RESOLUTION_REQUIRED, null, bundle);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.games;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.gms.R;
|
||||
|
||||
public class UpgradeActivity extends Activity {
|
||||
public static final String ACTION_PLAY_GAMES_UPGRADE = "com.google.android.gms.games.PLAY_GAMES_UPGRADE";
|
||||
public static final String EXTRA_GAME_PACACKE_NAME = "com.google.android.gms.games.GAME_PACKAGE_NAME";
|
||||
|
||||
private static final String TAG = "GmsUpgActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.games_info);
|
||||
|
||||
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
|
||||
lp.copyFrom(getWindow().getAttributes());
|
||||
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
getWindow().setAttributes(lp);
|
||||
|
||||
String packageName = getIntent().getStringExtra(EXTRA_GAME_PACACKE_NAME);
|
||||
|
||||
// receive package info
|
||||
PackageManager packageManager = getPackageManager();
|
||||
ApplicationInfo applicationInfo;
|
||||
try {
|
||||
applicationInfo = packageManager.getApplicationInfo(packageName, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
CharSequence appLabel = packageManager.getApplicationLabel(applicationInfo);
|
||||
Drawable appIcon = packageManager.getApplicationIcon(applicationInfo);
|
||||
|
||||
((ImageView) findViewById(R.id.app_icon)).setImageDrawable(appIcon);
|
||||
((TextView) findViewById(R.id.title)).setText(getString(R.string.games_info_title, appLabel));
|
||||
findViewById(android.R.id.button1).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue