Squashed commit

- Update all submodules, used sdk version, etc
- Settings UI rebuild
- Some GCM features and fixes
- Fix newest Cast Framework for some apps (tested with "ZDF Mediathek")

Fixes #224, #223, #145
This commit is contained in:
Marvin W 2016-11-16 00:52:49 +01:00
parent 8b61ba7e44
commit 5316e0220a
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
62 changed files with 1987 additions and 624 deletions

View File

@ -12,10 +12,9 @@ script:
- ./gradlew assemble
android:
components:
- platform-tools
- tools
- build-tools-24.0.2
- android-23
- platform-tools
- build-tools-24.0.3
- android-24
- extra-android-m2repository
before_cache:

View File

@ -19,14 +19,14 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
}
}
allprojects {
apply plugin: 'idea'
ext.androidBuildVersionTools = "24.0.2"
ext.androidBuildVersionTools = "24.0.3"
ext.isReleaseVersion = false
}
@ -34,7 +34,7 @@ def androidCompileSdk() { return 24 }
def androidTargetSdk() { return 24 }
def androidMinSdk() { return 10 }
def androidMinSdk() { return 9 }
def versionCode() {
def stdout = new ByteArrayOutputStream()

2
extern/GmsApi vendored

@ -1 +1 @@
Subproject commit 9ff42ae73a02ea971dc557f657b612113dfa6e24
Subproject commit 0b4f43c6a6a091dbdeb0ec544d533373a83ea319

2
extern/GmsLib vendored

@ -1 +1 @@
Subproject commit 4bffa5799a9ce406a268941e26fbb4a637d42446
Subproject commit a0300494f6cf65334c1d2f969571ef6069579bca

2
extern/UnifiedNlp vendored

@ -1 +1 @@
Subproject commit d9923a0d10edded17ad96d61e1e247f557bad652
Subproject commit 2f810b14fde67cb690b440b3249f1887435c2e92

View File

@ -0,0 +1 @@
extern/GmsApi/play-services-cast-framework-api

View File

@ -64,6 +64,9 @@ android {
// We are not allowed to freely choose the hundreds column as it defines the device type
versionCode(10084400 + x % 100 + ((int) (x / 100)) * 1000)
minSdkVersion androidMinSdk()
targetSdkVersion androidTargetSdk()
ndk {
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86"
}

View File

@ -17,10 +17,6 @@
<manifest package="com.google.android.gms"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="23"/>
<permission
android:name="com.google.android.c2dm.permission.RECEIVE"
android:label="@string/perm_c2dm_receive_label"
@ -224,6 +220,12 @@
</intent-filter>
</receiver>
<receiver android:name="org.microg.gms.gcm.UnregisterReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
</intent-filter>
</receiver>
<!-- Map -->
<provider
android:name="org.microg.gms.maps.data.SharedTileProvider"
@ -359,12 +361,59 @@
android:name="org.microg.gms.ui.SettingsActivity"
android:icon="@mipmap/ic_microg_settings"
android:label="@string/gms_settings_name"
android:theme="@style/SettingsTheme">
android:theme="@style/Theme.AppCompat.Settings.Dashboard">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="org.microg.gms.ui.AskPushPermission"
android:excludeFromRecents="true"
android:theme="@style/Theme.AppCompat.Light.Dialog.Alert"/>
<activity
android:name="org.microg.gms.ui.AboutFragment$AsActivity"
android:label="@string/pref_about_title"
android:theme="@style/Theme.AppCompat.Settings"/>
<activity
android:name="org.microg.gms.ui.GcmFragment$AsActivity"
android:label="@string/service_name_mcs"
android:theme="@style/Theme.AppCompat.Settings"/>
<activity
android:name="org.microg.gms.ui.GcmFragment$AdvancedAsActivity"
android:label="@string/service_name_mcs"
android:theme="@style/Theme.AppCompat.Settings"/>
<activity
android:name="org.microg.gms.ui.GcmAppFragment$AsActivity"
android:label="@string/service_name_mcs"
android:theme="@style/Theme.AppCompat.Settings"/>
<activity
android:name="org.microg.gms.ui.SelfCheckFragment$AsActivity"
android:label="@string/self_check_title"
android:theme="@style/Theme.AppCompat.Settings"/>
<activity
android:name="org.microg.gms.ui.AccountSettingsActivity"
android:theme="@style/Theme.AppCompat.Settings">
<intent-filter>
<action android:name="com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS"/>
<action android:name="com.google.android.gms.accountsettings.PRIVACY_SETTINGS"/>
<action android:name="com.google.android.gms.accountsettings.SECURITY_SETTINGS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name="org.microg.gms.ui.LocationSettingsActivity"
android:theme="@style/Theme.AppCompat.Settings">
<intent-filter>
<action android:name="com.google.android.gms.location.settings.LOCATION_HISTORY"/>
<action android:name="com.google.android.location.settings.LOCATION_REPORTING_SETTINGS"/>

View File

@ -59,7 +59,7 @@ public class AccountContentProvider extends ContentProvider {
if (getContext().checkCallingPermission(Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED)
throw new SecurityException("Access denied, missing GET_ACCOUNTS or EXTENDED_ACCESS permission");
}
if (PROVIDER_METHOD_GET_ACCOUNTS.equals(method) && getContext().getString(R.string.google_account_type).equals(arg)) {
if (PROVIDER_METHOD_GET_ACCOUNTS.equals(method) && AuthConstants.DEFAULT_ACCOUNT_TYPE.equals(arg)) {
Bundle result = new Bundle();
result.putParcelableArray(PROVIDER_EXTRA_ACCOUNTS, AccountManager.get(getContext()).getAccountsByType(arg));
return result;

View File

@ -56,7 +56,7 @@ public class AuthManager {
public String getAccountType() {
if (accountType == null)
accountType = context.getString(R.string.google_account_type);
accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
return accountType;
}

View File

@ -44,6 +44,7 @@ import android.widget.TextView;
import com.google.android.gms.R;
import org.json.JSONArray;
import org.microg.gms.auth.AuthConstants;
import org.microg.gms.auth.AuthManager;
import org.microg.gms.auth.AuthRequest;
import org.microg.gms.auth.AuthResponse;
@ -90,7 +91,7 @@ public class LoginActivity extends AssistantActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
accountType = getString(R.string.google_account_type);
accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
accountManager = AccountManager.get(LoginActivity.this);
inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
webView = createWebView(this);

View File

@ -30,6 +30,7 @@ import android.util.Log;
import com.google.android.gms.R;
import org.microg.gms.auth.AskPermissionActivity;
import org.microg.gms.auth.AuthConstants;
import org.microg.gms.auth.AuthManager;
import org.microg.gms.auth.AuthResponse;
import org.microg.gms.auth.login.LoginActivity;
@ -55,7 +56,7 @@ class AccountAuthenticator extends AbstractAccountAuthenticator {
public AccountAuthenticator(Context context) {
super(context);
this.context = context;
this.accountType = context.getString(R.string.google_account_type);
this.accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
}
@Override

View File

@ -23,6 +23,7 @@ import android.content.Context;
import com.google.android.gms.R;
import org.microg.gms.auth.AuthConstants;
import org.microg.gms.auth.AuthRequest;
import org.microg.gms.common.Constants;
import org.microg.gms.common.DeviceConfiguration;
@ -43,7 +44,7 @@ public class CheckinManager {
return null;
List<CheckinClient.Account> accounts = new ArrayList<CheckinClient.Account>();
AccountManager accountManager = AccountManager.get(context);
String accountType = context.getString(R.string.google_account_type);
String accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
for (Account account : accountManager.getAccountsByType(accountType)) {
String token = new AuthRequest()
.email(account.name).token(accountManager.getPassword(account))

View File

@ -28,6 +28,7 @@ import android.util.Log;
import com.google.android.gms.R;
import com.google.android.gms.checkin.internal.ICheckinService;
import org.microg.gms.auth.AuthConstants;
import org.microg.gms.gcm.McsService;
import org.microg.gms.people.PeopleManager;
@ -55,7 +56,7 @@ public class CheckinService extends IntentService {
LastCheckinInfo info = CheckinManager.checkin(this, intent.getBooleanExtra(EXTRA_FORCE_CHECKIN, false));
if (info != null) {
Log.d(TAG, "Checked in as " + Long.toHexString(info.androidId));
String accountType = getString(R.string.google_account_type);
String accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
for (Account account : AccountManager.get(this).getAccountsByType(accountType)) {
PeopleManager.loadUserInfo(this, account);
}

View File

@ -55,6 +55,6 @@ public class LastCheckinInfo {
.putLong(PREF_SECURITY_TOKEN, securityToken)
.putString(PREF_VERSION_INFO, versionInfo)
.putString(PREF_DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo)
.commit();
.apply();
}
}

View File

@ -86,7 +86,7 @@ public class HttpFormClient {
String result = new String(Utils.readStreamToEnd(connection.getInputStream()));
Log.d(TAG, "-- Response --\n" + result);
return parseResponse(tClass, connection.getHeaderFields(), result);
return parseResponse(tClass, connection, result);
}
private static String valueFromBoolVal(String value, Boolean boolVal, boolean truePresent, boolean falsePresent) {
@ -103,7 +103,8 @@ public class HttpFormClient {
}
}
private static <T> T parseResponse(Class<T> tClass, Map<String, List<String>> headerFields, String result) {
private static <T> T parseResponse(Class<T> tClass, HttpURLConnection connection, String result) throws IOException {
Map<String, List<String>> headerFields = connection.getHeaderFields();
T response;
try {
response = tClass.getConstructor().newInstance();
@ -153,6 +154,20 @@ public class HttpFormClient {
Log.w(TAG, e);
}
}
if (field.isAnnotationPresent(ResponseStatusCode.class) && field.getType() == int.class) {
try {
field.setInt(response, connection.getResponseCode());
} catch (IllegalAccessException e) {
Log.w(TAG, e);
}
}
if (field.isAnnotationPresent(ResponseStatusText.class) && field.getType() == String.class) {
try {
field.set(response, connection.getResponseMessage());
} catch (IllegalAccessException e) {
Log.w(TAG, e);
}
}
}
return response;
}
@ -217,4 +232,14 @@ public class HttpFormClient {
public @interface ResponseHeader {
public String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ResponseStatusCode {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ResponseStatusText {
}
}

View File

@ -0,0 +1,323 @@
/*
* Copyright (C) 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.
*/
package org.microg.gms.gcm;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class GcmDatabase extends SQLiteOpenHelper {
private static final String TAG = GcmDatabase.class.getSimpleName();
public static final String DB_NAME = "gcmstatus";
private static int DB_VERSION = 1;
private static final String CREATE_TABLE_APPS = "CREATE TABLE apps (" +
"package_name TEXT," +
"last_error TEXT DEFAULT ''," +
"last_message_timestamp INTEGER," +
"total_message_count INTEGER," +
"total_message_bytes INTEGER," +
"allow_register INTEGER DEFAULT 1," +
"wake_for_delivery INTEGER DEFAULT 1," +
"PRIMARY KEY (package_name));";
private static final String TABLE_APPS = "apps";
private static final String FIELD_PACKAGE_NAME = "package_name";
private static final String FIELD_LAST_ERROR = "last_error";
private static final String FIELD_LAST_MESSAGE_TIMESTAMP = "last_message_timestamp";
private static final String FIELD_TOTAL_MESSAGE_COUNT = "total_message_count";
private static final String FIELD_TOTAL_MESSAGE_BYTES = "total_message_bytes";
private static final String FIELD_ALLOW_REGISTER = "allow_register";
private static final String FIELD_WAKE_FOR_DELIVERY = "wake_for_delivery";
private static final String CREATE_TABLE_REGISTRATIONS = "CREATE TABLE registrations (" +
"package_name TEXT," +
"signature TEXT," +
"timestamp INTEGER," +
"register_id TEXT," +
"PRIMARY KEY (package_name, signature));";
private static final String TABLE_REGISTRATIONS = "registrations";
private static final String FIELD_SIGNATURE = "signature";
private static final String FIELD_TIMESTAMP = "timestamp";
private static final String FIELD_REGISTER_ID = "register_id";
private Context context;
public GcmDatabase(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.context = context;
}
public static class App {
public final String packageName;
public final String lastError;
public final long lastMessageTimestamp;
public final long totalMessageCount;
public final long totalMessageBytes;
public final boolean allowRegister;
public final boolean wakeForDelivery;
private App(Cursor cursor) {
packageName = cursor.getString(cursor.getColumnIndex(FIELD_PACKAGE_NAME));
lastError = cursor.getString(cursor.getColumnIndex(FIELD_LAST_ERROR));
lastMessageTimestamp = cursor.getLong(cursor.getColumnIndex(FIELD_LAST_MESSAGE_TIMESTAMP));
totalMessageCount = cursor.getLong(cursor.getColumnIndex(FIELD_TOTAL_MESSAGE_COUNT));
totalMessageBytes = cursor.getLong(cursor.getColumnIndex(FIELD_TOTAL_MESSAGE_BYTES));
allowRegister = cursor.getLong(cursor.getColumnIndex(FIELD_ALLOW_REGISTER)) == 1;
wakeForDelivery = cursor.getLong(cursor.getColumnIndex(FIELD_WAKE_FOR_DELIVERY)) == 1;
}
public boolean hasError() {
return !TextUtils.isEmpty(lastError);
}
}
public static class Registration {
public final String packageName;
public final String signature;
public final long timestamp;
public final String registerId;
public Registration(Cursor cursor) {
packageName = cursor.getString(cursor.getColumnIndex(FIELD_PACKAGE_NAME));
signature = cursor.getString(cursor.getColumnIndex(FIELD_SIGNATURE));
timestamp = cursor.getLong(cursor.getColumnIndex(FIELD_TIMESTAMP));
registerId = cursor.getString(cursor.getColumnIndex(FIELD_REGISTER_ID));
}
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_APPS);
db.execSQL(CREATE_TABLE_REGISTRATIONS);
importLegacyData(db);
}
public synchronized List<App> getAppList() {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_APPS, null, null, null, null, null, null);
if (cursor != null) {
List<App> result = new ArrayList<>();
while (cursor.moveToNext()) {
result.add(new App(cursor));
}
cursor.close();
return result;
}
return Collections.emptyList();
}
public synchronized List<Registration> getRegistrationList() {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_REGISTRATIONS, null, null, null, null, null, null);
if (cursor != null) {
List<Registration> result = new ArrayList<>();
while (cursor.moveToNext()) {
result.add(new Registration(cursor));
}
cursor.close();
return result;
}
return Collections.emptyList();
}
public synchronized List<Registration> getRegistrationsByApp(String packageName) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_REGISTRATIONS, null, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}, null, null, null);
if (cursor != null) {
List<Registration> result = new ArrayList<>();
while (cursor.moveToNext()) {
result.add(new Registration(cursor));
}
cursor.close();
return result;
}
return Collections.emptyList();
}
public synchronized void setAppAllowRegister(String packageName, boolean allowRegister) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(FIELD_ALLOW_REGISTER, allowRegister ? 1 : 0);
db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName});
}
public synchronized void setAppWakeForDelivery(String packageName, boolean wakeForDelivery) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(FIELD_WAKE_FOR_DELIVERY, wakeForDelivery ? 1 : 0);
db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName});
}
public synchronized void removeApp(String packageName) {
SQLiteDatabase db = getWritableDatabase();
db.delete(TABLE_REGISTRATIONS, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName});
db.delete(TABLE_APPS, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName});
}
public synchronized void noteAppRegistrationError(String packageName, String error) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(FIELD_LAST_ERROR, error);
db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName});
}
public synchronized void noteAppKnown(String packageName, boolean allowRegister) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
App app = getApp(db, packageName);
ContentValues cv = new ContentValues();
cv.put(FIELD_ALLOW_REGISTER, allowRegister);
if (app == null) {
cv.put(FIELD_PACKAGE_NAME, packageName);
db.insert(TABLE_APPS, null, cv);
} else {
db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName});
}
db.setTransactionSuccessful();
db.endTransaction();
}
public synchronized void noteAppMessage(String packageName, int numBytes) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
App app = getApp(db, packageName);
ContentValues cv = new ContentValues();
cv.put(FIELD_LAST_MESSAGE_TIMESTAMP, System.currentTimeMillis());
if (app == null) {
cv.put(FIELD_PACKAGE_NAME, packageName);
cv.put(FIELD_TOTAL_MESSAGE_COUNT, 1);
cv.put(FIELD_TOTAL_MESSAGE_BYTES, numBytes);
db.insert(TABLE_APPS, null, cv);
} else {
cv.put(FIELD_TOTAL_MESSAGE_COUNT, app.totalMessageCount + 1);
cv.put(FIELD_TOTAL_MESSAGE_BYTES, app.totalMessageBytes + numBytes);
db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName});
}
db.setTransactionSuccessful();
db.endTransaction();
}
public synchronized void noteAppRegistered(String packageName, String signature, String registrationId) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
App app = getApp(db, packageName);
if (app == null) {
ContentValues cv = new ContentValues();
cv.put(FIELD_PACKAGE_NAME, packageName);
db.insert(TABLE_APPS, null, cv);
} else {
ContentValues cv = new ContentValues();
cv.put(FIELD_LAST_ERROR, "");
db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName});
}
ContentValues cv = new ContentValues();
cv.put(FIELD_PACKAGE_NAME, packageName);
cv.put(FIELD_SIGNATURE, signature);
cv.put(FIELD_REGISTER_ID, registrationId);
cv.put(FIELD_TIMESTAMP, System.currentTimeMillis());
db.insertWithOnConflict(TABLE_REGISTRATIONS, null, cv, SQLiteDatabase.CONFLICT_REPLACE);
db.setTransactionSuccessful();
db.endTransaction();
}
public synchronized void noteAppUnregistered(String packageName, String signature) {
SQLiteDatabase db = getWritableDatabase();
db.delete(TABLE_REGISTRATIONS, FIELD_PACKAGE_NAME + " LIKE ? AND " + FIELD_SIGNATURE + " LIKE ?", new String[]{packageName, signature});
}
public App getApp(String packageName) {
return getApp(getReadableDatabase(), packageName);
}
private App getApp(SQLiteDatabase db, String packageName) {
Cursor cursor = db.query(TABLE_APPS, null, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}, null, null, null, "1");
if (cursor != null) {
try {
if (cursor.moveToNext()) {
return new App(cursor);
}
} finally {
cursor.close();
}
}
return null;
}
public Registration getRegistration(String packageName, String signature) {
return getRegistration(getReadableDatabase(), packageName, signature);
}
private Registration getRegistration(SQLiteDatabase db, String packageName, String signature) {
Cursor cursor = db.query(TABLE_REGISTRATIONS, null, FIELD_PACKAGE_NAME + " LIKE ? AND " + FIELD_SIGNATURE + " LIKE ?", new String[]{packageName, signature}, null, null, null, "1");
if (cursor != null) {
try {
if (cursor.moveToNext()) {
return new Registration(cursor);
}
} finally {
cursor.close();
}
}
return null;
}
@SuppressWarnings("deprecation")
private void importLegacyData(SQLiteDatabase db) {
db.beginTransaction();
GcmLegacyData legacyData = new GcmLegacyData(context);
for (GcmLegacyData.LegacyAppInfo appInfo : legacyData.getAppsInfo()) {
ContentValues cv = new ContentValues();
cv.put(FIELD_PACKAGE_NAME, appInfo.app);
cv.put(FIELD_TOTAL_MESSAGE_COUNT, legacyData.getAppMessageCount(appInfo.app));
cv.put(FIELD_LAST_ERROR, appInfo.hasUnregistrationError() ? "Unregistration error" : null);
db.insert(TABLE_APPS, null, cv);
cv.clear();
if (appInfo.isRegistered()) {
cv.put(FIELD_PACKAGE_NAME, appInfo.app);
cv.put(FIELD_SIGNATURE, appInfo.appSignature);
cv.put(FIELD_REGISTER_ID, appInfo.registerID);
db.insert(TABLE_REGISTRATIONS, null, cv);
}
}
db.setTransactionSuccessful();
db.endTransaction();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
throw new IllegalStateException("Upgrades not supported");
}
}

View File

@ -23,20 +23,22 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class GcmData {
@Deprecated
public class GcmLegacyData {
private static final String GCM_REGISTRATION_PREF = "gcm_registrations";
private static final String GCM_MESSAGES_PREF = "gcm_messages";
private static final String REMOVED = "%%REMOVED%%";
private static final String ERROR = "%%ERROR%%";
static final String REMOVED = "%%REMOVED%%";
static final String ERROR = "%%ERROR%%";
private Context context;
public GcmData(Context context) {
public GcmLegacyData(Context context) {
this.context = context;
}
public static class AppInfo implements Comparable<AppInfo> {
@Deprecated
public static class LegacyAppInfo implements Comparable<LegacyAppInfo> {
public String app = null;
public String appSignature = null;
public String registerID = null;
@ -46,7 +48,7 @@ public class GcmData {
private final int STATE_REGISTERED = 3;
private int state;
public AppInfo(String key, String value) {
public LegacyAppInfo(String key, String value) {
if (ERROR.equals(value)) {
state = STATE_ERROR;
} else if (REMOVED.equals(value)) {
@ -73,42 +75,19 @@ public class GcmData {
}
@Override
public int compareTo(AppInfo another) {
public int compareTo(LegacyAppInfo another) {
return app.compareTo(another.app);
}
}
public void noteAppRegistered(String app, String signature, String regId) {
getInfoSharedPreferences().edit().putString(app + ":" + signature, regId).apply();
}
public void noteAppRegistrationError(String app, String signature) {
getInfoSharedPreferences().edit().putString(app + ":" + signature, "-").apply();
}
public void noteAppUnregistered(String app, String signature) {
getInfoSharedPreferences().edit().putString(app + ":" + signature, REMOVED).apply();
}
public void noteAppUnregistrationError(String app, String signature) {
getInfoSharedPreferences().edit().putString(app + ":" + signature, ERROR).apply();
}
public void incrementAppMessageCount(String app, int i) {
int messageCount = getStatsSharedPreferences().getInt(app, 0);
getStatsSharedPreferences().edit().putInt(app, messageCount + i).apply();
}
@Deprecated
public int getAppMessageCount(String app) {
return getStatsSharedPreferences().getInt(app, 0);
}
public AppInfo getAppInfo(String app, String signature) {
return getAppInfo(app + ":" + signature);
}
public List<AppInfo> getAppsInfo() {
ArrayList<AppInfo> ret = new ArrayList<>();
@Deprecated
public List<LegacyAppInfo> getAppsInfo() {
ArrayList<LegacyAppInfo> ret = new ArrayList<>();
Set<String> keys = getInfoSharedPreferences().getAll().keySet();
for (String key : keys) {
ret.add(getAppInfo(key));
@ -116,8 +95,9 @@ public class GcmData {
return ret;
}
private AppInfo getAppInfo(String key) {
return new AppInfo(key, getInfoSharedPreferences().getString(key, ""));
@Deprecated
private LegacyAppInfo getAppInfo(String key) {
return new LegacyAppInfo(key, getInfoSharedPreferences().getString(key, ""));
}
private SharedPreferences getInfoSharedPreferences() {

View File

@ -25,9 +25,12 @@ import java.util.Collections;
import java.util.List;
public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String PREF_GCM_HEARTBEAT = "gcm_heartbeat_interval";
private static final String PREF_GCM_LOG = "gcm_full_log";
private static final String PREF_LAST_PERSISTENT_ID = "gcm_last_persistent_id";
public static final String PREF_HEARTBEAT = "gcm_heartbeat_interval";
public static final String PREF_FULL_LOG = "gcm_full_log";
public static final String PREF_LAST_PERSISTENT_ID = "gcm_last_persistent_id";
public static final String PREF_CONFIRM_NEW_APPS = "gcm_confirm_new_apps";
public static final String PREF_ENABLE_GCM = "gcm_enable_mcs_service";
private static GcmPrefs INSTANCE;
public static GcmPrefs get(Context context) {
@ -41,6 +44,8 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
private int heartbeatMs = 300000;
private boolean gcmLogEnabled = true;
private String lastPersistedId = "";
private boolean confirmNewApps = false;
private boolean gcmEnabled = false;
private SharedPreferences defaultPreferences;
@ -53,9 +58,11 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
}
public void update() {
heartbeatMs = Integer.parseInt(defaultPreferences.getString(PREF_GCM_HEARTBEAT, "300")) * 1000;
gcmLogEnabled = defaultPreferences.getBoolean(PREF_GCM_LOG, true);
heartbeatMs = Integer.parseInt(defaultPreferences.getString(PREF_HEARTBEAT, "300")) * 1000;
gcmLogEnabled = defaultPreferences.getBoolean(PREF_FULL_LOG, true);
lastPersistedId = defaultPreferences.getString(PREF_LAST_PERSISTENT_ID, "");
confirmNewApps = defaultPreferences.getBoolean(PREF_CONFIRM_NEW_APPS, false);
gcmEnabled = defaultPreferences.getBoolean(PREF_ENABLE_GCM, false);
}
public int getHeartbeatMs() {
@ -67,10 +74,18 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
update();
}
public boolean isGcmEnabled() {
return gcmEnabled;
}
public boolean isGcmLogEnabled() {
return gcmLogEnabled;
}
public boolean isConfirmNewApps() {
return confirmNewApps;
}
public List<String> getLastPersistedIds() {
if (lastPersistedId.isEmpty()) return Collections.emptyList();
return Arrays.asList(lastPersistedId.split("\\|"));

View File

@ -90,6 +90,7 @@ public class McsService extends Service implements Handler.Callback {
private static final int WAKELOCK_TIMEOUT = 5000;
private static long lastHeartbeatAckElapsedRealtime = -1;
private static long startTimestamp = 0;
private static Socket sslSocket;
private static McsInputStream inputStream;
@ -100,7 +101,7 @@ public class McsService extends Service implements Handler.Callback {
private static HandlerThread handlerThread;
private static Handler rootHandler;
private GcmData gcmStorage = new GcmData(this);
private GcmDatabase database;
private AlarmManager alarmManager;
private PowerManager powerManager;
@ -139,6 +140,7 @@ public class McsService extends Service implements Handler.Callback {
@Override
public void onCreate() {
super.onCreate();
database = new GcmDatabase(this);
heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
powerManager = (PowerManager) getSystemService(POWER_SERVICE);
@ -168,6 +170,10 @@ public class McsService extends Service implements Handler.Callback {
return true;
}
public static long getStartTimestamp() {
return startTimestamp;
}
public static void scheduleReconnect(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
long delay = getCurrentDelay();
@ -240,6 +246,7 @@ public class McsService extends Service implements Handler.Callback {
inputStream.start();
outputStream.start();
startTimestamp = System.currentTimeMillis();
lastHeartbeatAckElapsedRealtime = SystemClock.elapsedRealtime();
scheduleHeartbeat(this);
} catch (Exception e) {
@ -305,27 +312,30 @@ public class McsService extends Service implements Handler.Callback {
}
private void handleAppMessage(DataMessageStanza msg) {
database.noteAppMessage(msg.category, msg.getSerializedSize());
GcmDatabase.App app = database.getApp(msg.category);
Intent intent = new Intent();
intent.setAction(ACTION_C2DM_RECEIVE);
intent.setPackage(msg.category);
intent.putExtra(EXTRA_FROM, msg.from);
if (app.wakeForDelivery) intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
if (msg.token != null) intent.putExtra(EXTRA_COLLAPSE_KEY, msg.token);
for (AppData appData : msg.app_data) {
intent.putExtra(appData.key, appData.value);
}
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
// FIXME: #75
List<ResolveInfo> infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER);
for (ResolveInfo resolveInfo : infos) {
logd("Target: " + resolveInfo);
Intent targetIntent = new Intent(intent);
targetIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name));
sendOrderedBroadcast(targetIntent, msg.category + ".permission.C2D_MESSAGE");
}
if (infos.isEmpty())
logd("No target for message, wut?");
gcmStorage.incrementAppMessageCount(msg.category, 1);
List<ResolveInfo> infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER);
if (infos == null || infos.isEmpty()) {
logd("No target for message, wut?");
} else {
for (ResolveInfo resolveInfo : infos) {
logd("Target: " + resolveInfo);
Intent targetIntent = new Intent(intent);
targetIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name));
sendOrderedBroadcast(targetIntent, msg.category + ".permission.C2D_MESSAGE");
}
}
}
private void handleSelfMessage(DataMessageStanza msg) {

View File

@ -16,22 +16,32 @@
package org.microg.gms.gcm;
import android.app.AlertDialog;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Message;
import android.os.Messenger;
import android.text.Html;
import android.util.Log;
import org.microg.gms.checkin.CheckinService;
import org.microg.gms.checkin.LastCheckinInfo;
import org.microg.gms.common.PackageUtils;
import org.microg.gms.common.Utils;
import org.microg.gms.ui.AskPushPermission;
import java.io.IOException;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
import static android.os.Build.VERSION_CODES.JELLY_BEAN;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_UNREGISTER;
@ -40,6 +50,7 @@ import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
import static org.microg.gms.gcm.GcmConstants.EXTRA_DELETE;
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER;
import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT;
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
import static org.microg.gms.gcm.GcmConstants.EXTRA_RETRY_AFTER;
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER;
@ -49,30 +60,39 @@ public class PushRegisterService extends IntentService {
private static final String TAG = "GmsGcmRegisterSvc";
private static final String EXTRA_SKIP_TRY_CHECKIN = "skip_checkin";
private GcmData gcmStorage = new GcmData(this);
private GcmDatabase database;
private static boolean requestPending = false;
public PushRegisterService() {
super(TAG);
setIntentRedelivery(false);
}
public static RegisterResponse register(Context context, String app, String appSignature, String sender, String info) {
RegisterResponse response = register(context, app, appSignature, sender, info, false);
@Override
public void onCreate() {
super.onCreate();
database = new GcmDatabase(this);
}
public static RegisterResponse register(Context context, String packageName, String pkgSignature, String sender, String info) {
GcmDatabase database = new GcmDatabase(context);
RegisterResponse response = register(context, packageName, pkgSignature, sender, info, false);
String regId = response.token;
if (regId != null) {
(new GcmData(context)).noteAppRegistered(app, appSignature, regId);
database.noteAppRegistered(packageName, pkgSignature, regId);
} else {
(new GcmData(context)).noteAppRegistrationError(app, appSignature);
database.noteAppRegistrationError(packageName, response.responseText);
}
return response;
}
public static RegisterResponse unregister(Context context, String app, String appSignature, String sender, String info) {
RegisterResponse response = register(context, app, appSignature, sender, info, true);
if (!app.equals(response.deleted)) {
(new GcmData(context)).noteAppUnregistrationError(app, appSignature);
public static RegisterResponse unregister(Context context, String packageName, String pkgSignature, String sender, String info) {
GcmDatabase database = new GcmDatabase(context);
RegisterResponse response = register(context, packageName, pkgSignature, sender, info, true);
if (!packageName.equals(response.deleted)) {
database.noteAppRegistrationError(packageName, response.responseText);
} else {
(new GcmData(context)).noteAppUnregistered(app, appSignature);
database.noteAppUnregistered(packageName, pkgSignature);
}
return response;
}
@ -103,23 +123,49 @@ public class PushRegisterService extends IntentService {
@SuppressWarnings("deprecation")
private String packageFromPendingIntent(PendingIntent pi) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
if (SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return pi.getTargetPackage();
} else {
return pi.getCreatorPackage();
}
}
private void register(Intent intent) {
private void register(final Intent intent) {
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP);
String sender = intent.getStringExtra(EXTRA_SENDER);
String app = packageFromPendingIntent(pendingIntent);
final String packageName = packageFromPendingIntent(pendingIntent);
Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent.getExtras());
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
String appSignature = PackageUtils.firstSignatureDigest(this, app);
GcmDatabase.App app = database.getApp(packageName);
if (app == null && GcmPrefs.get(this).isConfirmNewApps()) {
try {
PackageManager pm = getPackageManager();
ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
Intent i = new Intent(this, AskPushPermission.class);
i.putExtra(EXTRA_PENDING_INTENT, intent);
i.putExtra(EXTRA_APP, packageName);
startActivity(i);
} catch (PackageManager.NameNotFoundException e) {
replyNotAvailable(this, intent, packageName);
}
} else if (app != null && !app.allowRegister) {
replyNotAvailable(this, intent, packageName);
} else {
registerAndReply(this, intent, packageName);
}
}
String regId = register(this, app, appSignature, sender, null).token;
public static void replyNotAvailable(Context context, Intent intent, String packageName) {
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
outIntent.putExtra(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE);
Log.d(TAG, "registration not allowed");
sendReply(context, intent, packageName, outIntent);
}
public static void registerAndReply(Context context, Intent intent, String packageName) {
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
String sender = intent.getStringExtra(EXTRA_SENDER);
String appSignature = PackageUtils.firstSignatureDigest(context, packageName);
String regId = register(context, packageName, appSignature, sender, null).token;
if (regId != null) {
outIntent.putExtra(EXTRA_REGISTRATION_ID, regId);
} else {
@ -127,6 +173,10 @@ public class PushRegisterService extends IntentService {
}
Log.d(TAG, "register[res]: " + outIntent + " extras=" + outIntent.getExtras());
sendReply(context, intent, packageName, outIntent);
}
private static void sendReply(Context context, Intent intent, String packageName, Intent outIntent) {
try {
if (intent.hasExtra(EXTRA_MESSENGER)) {
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
@ -139,8 +189,8 @@ public class PushRegisterService extends IntentService {
Log.w(TAG, e);
}
outIntent.setPackage(app);
sendOrderedBroadcast(outIntent, null);
outIntent.setPackage(packageName);
context.sendOrderedBroadcast(outIntent, null);
}
public static RegisterResponse register(Context context, String app, String appSignature, String sender, String info, boolean delete) {
@ -164,41 +214,28 @@ public class PushRegisterService extends IntentService {
private void unregister(Intent intent) {
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP);
String app = packageFromPendingIntent(pendingIntent);
String packageName = packageFromPendingIntent(pendingIntent);
Log.d(TAG, "unregister[req]: " + intent.toString() + " extras=" + intent.getExtras());
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
String appSignature = PackageUtils.firstSignatureDigest(this, app);
String appSignature = PackageUtils.firstSignatureDigest(this, packageName);
if (!gcmStorage.getAppInfo(app, appSignature).isRemoved()) {
outIntent.putExtra(EXTRA_UNREGISTERED, app);
if (database.getRegistration(packageName, appSignature) == null) {
outIntent.putExtra(EXTRA_UNREGISTERED, packageName);
} else {
RegisterResponse response = unregister(this, app, appSignature, null, null);
if (!app.equals(response.deleted)) {
RegisterResponse response = unregister(this, packageName, appSignature, null, null);
if (!packageName.equals(response.deleted)) {
outIntent.putExtra(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE);
if (response.retryAfter != null && !response.retryAfter.contains(":")) {
outIntent.putExtra(EXTRA_RETRY_AFTER, Long.parseLong(response.retryAfter));
}
} else {
outIntent.putExtra(EXTRA_UNREGISTERED, app);
outIntent.putExtra(EXTRA_UNREGISTERED, packageName);
}
}
Log.d(TAG, "unregister[res]: " + outIntent.toString() + " extras=" + outIntent.getExtras());
try {
if (intent.hasExtra(EXTRA_MESSENGER)) {
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
Message message = Message.obtain();
message.obj = outIntent;
messenger.send(message);
return;
}
} catch (Exception e) {
Log.w(TAG, e);
}
outIntent.setPackage(app);
sendOrderedBroadcast(outIntent, null);
sendReply(this, intent, packageName, outIntent);
}
}

View File

@ -17,6 +17,7 @@
package org.microg.gms.gcm;
import org.microg.gms.common.HttpFormClient.ResponseHeader;
import org.microg.gms.common.HttpFormClient.ResponseStatusText;
import static org.microg.gms.common.HttpFormClient.ResponseField;
@ -27,6 +28,8 @@ public class RegisterResponse {
public String retryAfter;
@ResponseField("deleted")
public String deleted;
@ResponseStatusText
public String responseText;
@Override
public String toString() {

View File

@ -32,14 +32,13 @@ import static org.microg.gms.gcm.McsConstants.EXTRA_REASON;
public class TriggerReceiver extends WakefulBroadcastReceiver {
private static final String TAG = "GmsGcmTrigger";
private static final String PREF_ENABLE_GCM = "gcm_enable_mcs_service";
@Override
public void onReceive(Context context, Intent intent) {
boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction());
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_ENABLE_GCM, false) || force) {
if (GcmPrefs.get(context).isGcmEnabled() || force) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
McsService.resetCurrentDelay();
}

View File

@ -0,0 +1,21 @@
package org.microg.gms.gcm;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_DATA_REMOVED;
public class UnregisterReceiver extends BroadcastReceiver {
private static final String TAG = "GmsGcmUnregisterRcvr";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Package removed: " + intent);
if (ACTION_PACKAGE_REMOVED.contains(intent.getAction()) && intent.getBooleanExtra(EXTRA_DATA_REMOVED, false)) {
Log.d(TAG, "Package removed: " + intent.getData());
}
}
}

View File

@ -33,6 +33,7 @@ import com.google.android.gms.people.internal.IPeopleCallbacks;
import com.google.android.gms.people.internal.IPeopleService;
import com.google.android.gms.people.model.AccountMetadata;
import org.microg.gms.auth.AuthConstants;
import org.microg.gms.common.NonCancelToken;
import org.microg.gms.common.PackageUtils;
@ -53,7 +54,7 @@ public class PeopleServiceImpl extends IPeopleService.Stub {
PackageUtils.assertExtendedAccess(context);
AccountManager accountManager = AccountManager.get(context);
Bundle accountMetadata = new Bundle();
String accountType = context.getString(R.string.google_account_type);
String accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE;
for (Account account : accountManager.getAccountsByType(accountType)) {
if (accountName == null || account.name.equals(accountName)) {
accountMetadata.putParcelable(account.name, new AccountMetadata());

View File

@ -0,0 +1,50 @@
/*
* 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.
*/
package org.microg.gms.ui;
import android.support.v4.app.Fragment;
import org.microg.tools.ui.AbstractAboutFragment;
import org.microg.tools.ui.AbstractSettingsActivity;
import java.util.List;
public class AboutFragment extends AbstractAboutFragment {
@Override
protected void collectLibraries(List<AbstractAboutFragment.Library> libraries) {
libraries.add(new AbstractAboutFragment.Library("org.microg.gms.api", "microG GmsApi", "Apache License 2.0, microG Team"));
libraries.add(new AbstractAboutFragment.Library("org.microg.safeparcel", "microG SafeParcel", "Apache License 2.0, microG Team"));
libraries.add(new AbstractAboutFragment.Library("org.microg.nlp", "microG UnifiedNlp", "Apache License 2.0, microG Team"));
libraries.add(new AbstractAboutFragment.Library("org.microg.nlp.api", "microG UnifiedNlp Api", "Apache License 2.0, microG Team"));
libraries.add(new AbstractAboutFragment.Library("org.microg.wearable", "microG Wearable", "Apache License 2.0, microG Team"));
libraries.add(new AbstractAboutFragment.Library("de.hdodenhof.circleimageview", "CircleImageView", "Apache License 2.0, Henning Dodenhof"));
libraries.add(new AbstractAboutFragment.Library("org.oscim.android", "V™", "GNU LGPLv3, Hannes Janetzek and devemux86"));
libraries.add(new AbstractAboutFragment.Library("com.squareup.wire", "Wire Protocol Buffers", "Apache License 2.0, Square Inc."));
}
public static class AsActivity extends AbstractSettingsActivity {
public AsActivity() {
showHomeAsUp = true;
}
@Override
protected Fragment getFragment() {
return new AboutFragment();
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 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.
*/
package org.microg.gms.ui;
import com.google.android.gms.R;
import org.microg.tools.ui.AbstractSettingsActivity;
public class AccountSettingsActivity extends AbstractSettingsActivity {
public AccountSettingsActivity() {
preferencesResource = R.xml.preferences_account;
}
}

View File

@ -0,0 +1,88 @@
package org.microg.gms.ui;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.text.Html;
import android.view.View;
import android.widget.TextView;
import com.google.android.gms.R;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.PushRegisterService;
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT;
public class AskPushPermission extends FragmentActivity {
private GcmDatabase database;
private String packageName;
private Intent intent;
private boolean answered;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
database = new GcmDatabase(this);
packageName = getIntent().getStringExtra(EXTRA_APP);
intent = getIntent().getParcelableExtra(EXTRA_PENDING_INTENT);
if (database.getApp(packageName) != null) {
finish();
return;
}
setContentView(R.layout.ask_gcm);
try {
PackageManager pm = getPackageManager();
final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
CharSequence label = pm.getApplicationLabel(info);
((TextView) findViewById(R.id.permission_message)).setText(Html.fromHtml("Allow <b>" + label + "</b> to register for push notifications?"));
findViewById(R.id.permission_allow_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (answered) return;
database.noteAppKnown(packageName, true);
answered = true;
new Thread(new Runnable() {
@Override
public void run() {
PushRegisterService.registerAndReply(AskPushPermission.this, intent, packageName);
}
}).start();
finish();
}
});
findViewById(R.id.permission_deny_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (answered) return;
database.noteAppKnown(packageName, false);
answered = true;
PushRegisterService.replyNotAvailable(AskPushPermission.this, intent, packageName);
finish();
}
});
} catch (PackageManager.NameNotFoundException e) {
finish();
}
}
@Override
protected void onStop() {
super.onStop();
if (!answered) {
PushRegisterService.replyNotAvailable(AskPushPermission.this, intent, packageName);
answered = true;
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 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.
*/
package org.microg.gms.ui;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.PowerManager;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.view.View;
import com.google.android.gms.R;
import org.microg.gms.gcm.GcmPrefs;
import org.microg.tools.ui.Condition;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.GET_ACCOUNTS;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT;
public class Conditions {
public static final Condition GCM_BATTERY_OPTIMIZATIONS = new Condition.Builder()
.title(R.string.cond_gcm_bat_title)
.summary(R.string.cond_gcm_bat_summary)
.evaluation(new Condition.Evaluation() {
@Override
public boolean isActive(Context context) {
if (SDK_INT < 23) return false;
if (!GcmPrefs.get(context).isGcmEnabled()) return false;
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
return !pm.isIgnoringBatteryOptimizations(context.getPackageName());
}
})
.firstAction(R.string.cond_gcm_bat_action, new View.OnClickListener() {
@Override
public void onClick(View v) {
if (SDK_INT < 23) return;
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + v.getContext().getPackageName()));
v.getContext().startActivity(intent);
}
}).build();
private static final String[] REQUIRED_PERMISSIONS = new String[]{ACCESS_COARSE_LOCATION, WRITE_EXTERNAL_STORAGE, GET_ACCOUNTS, READ_PHONE_STATE};
public static final Condition PERMISSIONS = new Condition.Builder()
.title(R.string.cond_perm_title)
.summary(R.string.cond_perm_summary)
.evaluation(new Condition.Evaluation() {
@Override
public boolean isActive(Context context) {
for (String permission : REQUIRED_PERMISSIONS) {
if (ContextCompat.checkSelfPermission(context, permission) != PERMISSION_GRANTED)
return true;
}
return false;
}
})
.firstAction(R.string.cond_perm_action, new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v.getContext() instanceof Activity) {
ActivityCompat.requestPermissions((Activity) v.getContext(), REQUIRED_PERMISSIONS, 0);
}
}
}).build();
}

View File

@ -0,0 +1,202 @@
package org.microg.gms.ui;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v14.preference.SwitchPreference;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.gms.R;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.PushRegisterService;
import org.microg.tools.ui.AbstractSettingsActivity;
import org.microg.tools.ui.ResourceSettingsFragment;
import java.util.List;
import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
public class GcmAppFragment extends ResourceSettingsFragment {
public static final String EXTRA_PACKAGE_NAME = "package_name";
public static final String PREF_WAKE_FOR_DELIVERY = "gcm_app_wake_for_delivery";
public static final String PREF_ALLOW_REGISTER = "gcm_app_allow_register";
public static final String PREF_REGISTER_DETAILS = "gcm_app_register_details";
public static final String PREF_MESSAGE_DETAILS = "gcm_app_message_details";
protected String packageName;
private String appName;
private GcmDatabase database;
public GcmAppFragment() {
preferencesResource = R.xml.preferences_gcm_app_detail;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
packageName = getArguments().getString(EXTRA_PACKAGE_NAME);
AbstractSettingsActivity activity = (AbstractSettingsActivity) getActivity();
if (packageName != null && activity != null) {
activity.setCustomBarLayout(R.layout.app_bar);
try {
PackageManager pm = activity.getPackageManager();
ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
((ImageView) activity.findViewById(R.id.app_icon)).setImageDrawable(pm.getApplicationIcon(info));
appName = pm.getApplicationLabel(info).toString();
((TextView) activity.findViewById(R.id.app_name)).setText(appName);
View view = activity.findViewById(R.id.app_bar);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", packageName, null);
intent.setData(uri);
getContext().startActivity(intent);
}
});
view.setClickable(true);
} catch (Exception e) {
((TextView) activity.findViewById(R.id.app_name)).setText(packageName);
}
}
database = new GcmDatabase(getContext());
updateAppDetails();
}
@Override
public void onResume() {
super.onResume();
if (database != null) {
updateAppDetails();
}
}
private void updateAppDetails() {
GcmDatabase.App app = database.getApp(packageName);
PreferenceScreen root = getPreferenceScreen();
SwitchPreference wakeForDelivery = (SwitchPreference) root.findPreference(PREF_WAKE_FOR_DELIVERY);
wakeForDelivery.setChecked(app.wakeForDelivery);
wakeForDelivery.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue instanceof Boolean) {
database.setAppWakeForDelivery(packageName, (Boolean) newValue);
return true;
}
return false;
}
});
SwitchPreference allowRegister = (SwitchPreference) root.findPreference(PREF_ALLOW_REGISTER);
allowRegister.setChecked(app.allowRegister);
allowRegister.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue instanceof Boolean) {
database.setAppAllowRegister(packageName, (Boolean) newValue);
return true;
}
return false;
}
});
Preference registerDetails = root.findPreference(PREF_REGISTER_DETAILS);
final List<GcmDatabase.Registration> registrations = database.getRegistrationsByApp(packageName);
if (registrations.isEmpty()) {
registerDetails.setTitle("");
registerDetails.setSelectable(false);
registerDetails.setSummary(R.string.gcm_not_registered);
} else {
StringBuilder sb = new StringBuilder();
for (GcmDatabase.Registration registration : registrations) {
if (sb.length() != 0) sb.append("\n");
if (registration.timestamp == 0) {
sb.append(getString(R.string.gcm_registered));
} else {
sb.append(getString(R.string.gcm_registered_since, DateUtils.getRelativeDateTimeString(getContext(), registration.timestamp, MINUTE_IN_MILLIS, WEEK_IN_MILLIS, FORMAT_SHOW_TIME)));
}
}
registerDetails.setTitle(R.string.gcm_unregister_app);
registerDetails.setSummary(sb.toString());
registerDetails.setSelectable(true);
registerDetails.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
new AlertDialog.Builder(getContext())
.setTitle(String.format(getString(R.string.gcm_unregister_confirm_title), appName))
.setMessage(R.string.gcm_unregister_confirm_message)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new Thread(new Runnable() {
@Override
public void run() {
for (GcmDatabase.Registration registration : registrations) {
PushRegisterService.unregister(getContext(), registration.packageName, registration.signature, null, null);
}
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
updateAppDetails();
}
});
}
}).start();
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Do nothing
}
}).create().show();
return true;
}
});
}
Preference messageDetails = root.findPreference(PREF_MESSAGE_DETAILS);
if (app.totalMessageCount == 0) {
messageDetails.setSummary(R.string.gcm_no_message_yet);
} else {
String s = getString(R.string.gcm_messages_counter, app.totalMessageCount, app.totalMessageBytes);
if (app.lastMessageTimestamp != 0) {
s += "\n" + getString(R.string.gcm_last_message_at, DateUtils.getRelativeDateTimeString(getContext(), app.lastMessageTimestamp, MINUTE_IN_MILLIS, WEEK_IN_MILLIS, FORMAT_SHOW_TIME));
}
messageDetails.setSummary(s);
}
}
public static class AsActivity extends AbstractSettingsActivity {
public AsActivity() {
showHomeAsUp = true;
}
@Override
protected Fragment getFragment() {
GcmAppFragment fragment = new GcmAppFragment();
fragment.setArguments(getIntent().getExtras());
return fragment;
}
}
}

View File

@ -0,0 +1,262 @@
/*
* Copyright 2013-2015 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.ui;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.PreferenceViewHolder;
import android.support.v7.widget.SwitchCompat;
import android.text.format.DateUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.google.android.gms.R;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.GcmPrefs;
import org.microg.gms.gcm.McsService;
import org.microg.tools.ui.AbstractSettingsActivity;
import org.microg.tools.ui.DimmableIconPreference;
import org.microg.tools.ui.ResourceSettingsFragment;
import org.microg.tools.ui.SwitchBar;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
public class GcmFragment extends ResourceSettingsFragment implements SwitchBar.OnSwitchChangeListener {
public static final String PREF_GCM_STATUS = "pref_gcm_status";
public static final String PREF_GCM_APPS = "gcm_apps";
private GcmDatabase database;
private SwitchBar switchBar;
private SwitchCompat switchCompat;
private boolean listenerSetup = false;
private final int MENU_ADVANCED = Menu.FIRST;
public GcmFragment() {
preferencesResource = R.xml.preferences_gcm;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
AbstractSettingsActivity activity = (AbstractSettingsActivity) getActivity();
setHasOptionsMenu(true);
switchBar = activity.getSwitchBar();
switchBar.show();
switchCompat = switchBar.getSwitch();
switchBar.setChecked(GcmPrefs.get(getContext()).isGcmEnabled());
}
@Override
public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) {
super.onCreatePreferencesFix(savedInstanceState, rootKey);
database = new GcmDatabase(getContext());
updateContent();
}
@Override
public void onDestroyView() {
super.onDestroyView();
switchBar.hide();
}
@Override
public void onResume() {
super.onResume();
if (!listenerSetup) {
switchBar.addOnSwitchChangeListener(this);
listenerSetup = true;
}
updateContent();
}
@Override
public void onPause() {
if (listenerSetup) {
switchBar.removeOnSwitchChangeListener(this);
listenerSetup = false;
}
super.onPause();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(0, MENU_ADVANCED, 0, R.string.menu_advanced);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ADVANCED:
Intent intent = new Intent(getContext(), AdvancedAsActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onSwitchChanged(SwitchCompat switchView, boolean isChecked) {
if (switchView == switchCompat) {
getPreferenceManager().getSharedPreferences().edit().putBoolean(GcmPrefs.PREF_ENABLE_GCM, isChecked).apply();
}
}
private static void addPreferencesSorted(List<Preference> prefs, PreferenceGroup container) {
// If there's some items to display, sort the items and add them to the container.
Collections.sort(prefs, new Comparator<Preference>() {
@Override
public int compare(Preference lhs, Preference rhs) {
return lhs.getTitle().toString().compareTo(rhs.getTitle().toString());
}
});
for (Preference entry : prefs) {
container.addPreference(entry);
}
}
private void updateContent() {
PreferenceScreen root = getPreferenceScreen();
if (McsService.isConnected()) {
root.findPreference(PREF_GCM_STATUS).setSummary(getString(R.string.gcm_state_connected, DateUtils.getRelativeTimeSpanString(McsService.getStartTimestamp(), System.currentTimeMillis(), 0)));
} else {
root.findPreference(PREF_GCM_STATUS).setSummary(getString(R.string.gcm_state_disconnected));
}
PreferenceCategory appList = (PreferenceCategory) root.findPreference(PREF_GCM_APPS);
appList.removeAll();
List<GcmDatabase.App> list = database.getAppList();
if (!list.isEmpty()) {
List<Preference> appListPrefs = new ArrayList<>();
PackageManager pm = getContext().getPackageManager();
for (GcmDatabase.App app : list) {
try {
pm.getApplicationInfo(app.packageName, 0);
appListPrefs.add(new GcmAppPreference(getPreferenceManager().getContext(), app));
} catch (PackageManager.NameNotFoundException e) {
final List<GcmDatabase.Registration> registrations = database.getRegistrationsByApp(app.packageName);
if (registrations.isEmpty()) {
database.removeApp(app.packageName);
} else {
appListPrefs.add(new GcmAppPreference(getPreferenceManager().getContext(), app));
}
}
}
addPreferencesSorted(appListPrefs, appList);
} else {
// If there's no item to display, add a "None" item.
Preference banner = new Preference(getPreferenceManager().getContext());
banner.setLayoutResource(R.layout.list_no_item);
banner.setTitle(R.string.list_no_item_none);
banner.setSelectable(false);
appList.addPreference(banner);
}
}
public static class GcmAppPreference extends DimmableIconPreference implements Preference.OnPreferenceClickListener {
private GcmDatabase database;
private GcmDatabase.App app;
public GcmAppPreference(Context context, GcmDatabase.App app) {
super(context);
this.app = app;
this.database = new GcmDatabase(context);
setKey(app.packageName);
PackageManager packageManager = context.getPackageManager();
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(app.packageName, 0);
setTitle(packageManager.getApplicationLabel(applicationInfo));
setIcon(packageManager.getApplicationIcon(applicationInfo));
} catch (PackageManager.NameNotFoundException e) {
setTitle(app.packageName);
}
setOnPreferenceClickListener(this);
updateViewDetails();
}
private void updateViewDetails() {
if (database.getRegistrationsByApp(app.packageName).isEmpty()) {
setSummary(R.string.gcm_not_registered);
} else if (app.lastMessageTimestamp > 0) {
setSummary(getContext().getString(R.string.gcm_last_message_at, DateUtils.getRelativeDateTimeString(getContext(), app.lastMessageTimestamp, MINUTE_IN_MILLIS, WEEK_IN_MILLIS, FORMAT_SHOW_TIME)));
} else {
setSummary(R.string.gcm_no_message_yet);
}
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
updateViewDetails();
super.onBindViewHolder(view);
}
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(getContext(), GcmAppFragment.AsActivity.class);
intent.putExtra(GcmAppFragment.EXTRA_PACKAGE_NAME, app.packageName);
getContext().startActivity(intent);
return true;
}
}
public static class AsActivity extends AbstractSettingsActivity {
public AsActivity() {
showHomeAsUp = true;
}
@Override
protected Fragment getFragment() {
return new GcmFragment();
}
}
public static class AdvancedAsActivity extends AbstractSettingsActivity {
public AdvancedAsActivity() {
showHomeAsUp = true;
preferencesResource = R.xml.preferences_gcm_advanced;
}
}
}

View File

@ -1,163 +0,0 @@
/*
* Copyright 2013-2015 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.ui;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.google.android.gms.R;
import org.microg.gms.gcm.GcmData;
import org.microg.gms.gcm.PushRegisterService;
import java.util.ArrayList;
public class GcmRegisteredAppsFragment extends Fragment {
private AppsAdapter appsAdapter = null;
private GcmData gcmStorage = null;
@Override
@NonNull
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
gcmStorage = new GcmData(getContext());
View view = inflater.inflate(R.layout.gcm_apps_list, container, false);
ListView listView = (ListView) view.findViewById(R.id.list_view);
registerForContextMenu(listView);
updateListView();
listView.setAdapter(appsAdapter);
return listView;
}
@Override
public void onResume() {
super.onResume();
updateListView();
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
GcmData.AppInfo appInfo = appsAdapter.getItem(((AdapterView.AdapterContextMenuInfo) menuInfo).position);
MenuInflater menuInflater = getActivity().getMenuInflater();
menuInflater.inflate(R.menu.gcm_app, menu);
PackageManager packageManager = getContext().getPackageManager();
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(appInfo.app, 0);
menu.setHeaderTitle(packageManager.getApplicationLabel(applicationInfo));
} catch (PackageManager.NameNotFoundException e) {
menu.setHeaderTitle(appInfo.app);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
final GcmData.AppInfo appInfo = appsAdapter.getItem(info.position);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
PushRegisterService.unregister(getContext(), appInfo.app, appInfo.appSignature, null, null);
return null;
}
protected void onPostExecute(Void result) {
updateListView();
}
}.execute();
return true;
}
public synchronized void updateListView() {
ArrayList<GcmData.AppInfo> registeredApps = new ArrayList<GcmData.AppInfo>();
for (GcmData.AppInfo appInfo : gcmStorage.getAppsInfo()) {
if (appInfo.isRegistered()) {
registeredApps.add(appInfo);
}
}
if (appsAdapter != null) {
appsAdapter.update(registeredApps.toArray(new GcmData.AppInfo[registeredApps.size()]));
} else {
appsAdapter = new AppsAdapter(getContext(), registeredApps.toArray(new GcmData.AppInfo[registeredApps.size()]));
}
}
private class AppsAdapter extends ArrayAdapter<GcmData.AppInfo> {
public AppsAdapter(Context context, GcmData.AppInfo[] libraries) {
super(context, R.layout.gcm_app, R.id.title);
addAll(libraries);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
ImageView image = (ImageView) v.findViewById(R.id.image);
TextView title = (TextView) v.findViewById(R.id.title);
TextView sub = (TextView) v.findViewById(R.id.sub);
TextView warning = (TextView) v.findViewById(R.id.warning);
GcmData.AppInfo appInfo = getItem(position);
PackageManager packageManager = getContext().getPackageManager();
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(appInfo.app, 0);
title.setText(packageManager.getApplicationLabel(applicationInfo).toString());
image.setImageDrawable(packageManager.getApplicationIcon(applicationInfo));
if (appInfo.hasUnregistrationError()) {
warning.setVisibility(View.VISIBLE);
warning.setText(getString(R.string.gcm_app_error_unregistering));
} else {
warning.setVisibility(View.GONE);
}
} catch (PackageManager.NameNotFoundException e) {
title.setText(appInfo.app);
image.setImageDrawable(null);
warning.setVisibility(View.VISIBLE);
warning.setText(getString(R.string.gcm_app_not_installed_anymore));
}
sub.setText(getString(R.string.gcm_messages_received_no, gcmStorage.getAppMessageCount(appInfo.app)));
return v;
}
public void update(GcmData.AppInfo[] appInfos) {
setNotifyOnChange(false);
clear();
addAll(appInfos);
notifyDataSetChanged();
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 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.
*/
package org.microg.gms.ui;
import android.app.Activity;
public class LocationSettingsActivity extends Activity {
}

View File

@ -25,6 +25,7 @@ import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
@ -153,7 +154,7 @@ public class PlacePickerActivity extends AppCompatActivity implements Map.Update
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.pick_place, menu);
SearchView searchView = (SearchView) menu.findItem(R.id.menu_action_search).getActionView();
SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.menu_action_search));
// TODO: search
return true;
}

View File

@ -0,0 +1,83 @@
/*
* 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.
*/
package org.microg.gms.ui;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import org.microg.tools.selfcheck.InstalledPackagesChecks;
import org.microg.tools.selfcheck.NlpOsCompatChecks;
import org.microg.tools.selfcheck.NlpStatusChecks;
import org.microg.tools.selfcheck.PermissionCheckGroup;
import org.microg.tools.selfcheck.RomSpoofSignatureChecks;
import org.microg.tools.selfcheck.SelfCheckGroup;
import org.microg.tools.selfcheck.SystemChecks;
import org.microg.tools.ui.AbstractSelfCheckFragment;
import org.microg.tools.ui.AbstractSettingsActivity;
import java.util.List;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.GET_ACCOUNTS;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
public class SelfCheckFragment extends AbstractSelfCheckFragment {
@Override
protected void prepareSelfCheckList(List<SelfCheckGroup> checks) {
checks.add(new RomSpoofSignatureChecks());
checks.add(new InstalledPackagesChecks());
if (SDK_INT > LOLLIPOP_MR1) {
checks.add(new PermissionCheckGroup(ACCESS_COARSE_LOCATION, WRITE_EXTERNAL_STORAGE, GET_ACCOUNTS, READ_PHONE_STATE));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checks.add(new SystemChecks());
}
checks.add(new NlpOsCompatChecks());
checks.add(new NlpStatusChecks());
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
reset(LayoutInflater.from(getContext()));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SystemChecks.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
reset(LayoutInflater.from(getContext()));
else
super.onActivityResult(requestCode, resultCode, data);
}
public static class AsActivity extends AbstractSettingsActivity {
public AsActivity() {
showHomeAsUp = true;
}
@Override
protected Fragment getFragment() {
return new SelfCheckFragment();
}
}
}

View File

@ -16,137 +16,51 @@
package org.microg.gms.ui;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.preference.Preference;
import android.support.annotation.NonNull;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.preference.PreferenceFragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.preference.PreferenceManager;
import com.google.android.gms.R;
import org.microg.tools.selfcheck.InstalledPackagesChecks;
import org.microg.tools.selfcheck.NlpOsCompatChecks;
import org.microg.tools.selfcheck.NlpStatusChecks;
import org.microg.tools.selfcheck.PermissionCheckGroup;
import org.microg.tools.selfcheck.RomSpoofSignatureChecks;
import org.microg.tools.selfcheck.SelfCheckGroup;
import org.microg.tools.selfcheck.SystemChecks;
import org.microg.tools.ui.AbstractAboutFragment;
import org.microg.tools.ui.AbstractSelfCheckFragment;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.GcmPrefs;
import org.microg.tools.ui.AbstractDashboardActivity;
import org.microg.tools.ui.ResourceSettingsFragment;
import java.util.List;
public class SettingsActivity extends AbstractDashboardActivity {
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.GET_ACCOUNTS;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
public SettingsActivity() {
preferencesResource = R.xml.preferences_start;
addCondition(Conditions.GCM_BATTERY_OPTIMIZATIONS);
addCondition(Conditions.PERMISSIONS);
}
public class SettingsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_activity);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
getSupportFragmentManager().beginTransaction()
.replace(R.id.content_wrapper, new MyPreferenceFragment())
.commit();
protected Fragment getFragment() {
return new FragmentImpl();
}
public static class MyPreferenceFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public static class FragmentImpl extends ResourceSettingsFragment {
addPreferencesFromResource(R.xml.gms_preferences);
public static final String PREF_ABOUT = "pref_about";
public static final String PREF_GCM = "pref_gcm";
findPreference(getString(R.string.self_check_title))
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
getFragmentManager().beginTransaction()
.addToBackStack("root")
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.replace(R.id.content_wrapper, new MySelfCheckFragment())
.commit();
return true;
}
});
findPreference(getString(R.string.pref_about_title))
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
getFragmentManager().beginTransaction()
.addToBackStack("root")
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.replace(R.id.content_wrapper, new MyAboutFragment())
.commit();
return true;
}
});
findPreference(getString(R.string.pref_gcm_apps))
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
getFragmentManager().beginTransaction()
.addToBackStack("root")
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.replace(R.id.content_wrapper, new GcmRegisteredAppsFragment())
.commit();
return true;
}
});
public FragmentImpl() {
preferencesResource = R.xml.preferences_start;
}
}
public static class MySelfCheckFragment extends AbstractSelfCheckFragment {
@Override
protected void prepareSelfCheckList(List<SelfCheckGroup> checks) {
checks.add(new RomSpoofSignatureChecks());
checks.add(new InstalledPackagesChecks());
if (SDK_INT > LOLLIPOP_MR1) {
checks.add(new PermissionCheckGroup(ACCESS_COARSE_LOCATION, WRITE_EXTERNAL_STORAGE, GET_ACCOUNTS, READ_PHONE_STATE));
public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) {
super.onCreatePreferencesFix(savedInstanceState, rootKey);
PreferenceManager prefs = getPreferenceManager();
prefs.findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext())));
if (GcmPrefs.get(getContext()).isGcmEnabled()) {
int regCount = new GcmDatabase(getContext()).getRegistrationList().size();
prefs.findPreference(PREF_GCM).setSummary(getString(R.string.v7_preference_on) + " / " + getContext().getString(R.string.gcm_registered_apps_counter, regCount));
} else {
prefs.findPreference(PREF_GCM).setSummary(R.string.v7_preference_off);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checks.add(new SystemChecks());
}
checks.add(new NlpOsCompatChecks());
checks.add(new NlpStatusChecks());
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
reset(LayoutInflater.from(getContext()));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SystemChecks.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
reset(LayoutInflater.from(getContext()));
else
super.onActivityResult(requestCode, resultCode, data);
}
}
public static class MyAboutFragment extends AbstractAboutFragment {
@Override
protected void collectLibraries(List<Library> libraries) {
libraries.add(new Library("org.microg.gms.api", "microG GmsApi", "Apache License 2.0 by microG Team"));
libraries.add(new Library("org.microg.safeparcel", "microG SafeParcel", "Apache License 2.0 by microG Team"));
libraries.add(new Library("org.microg.nlp", "microG UnifiedNlp", "Apache License 2.0 by microG Team"));
libraries.add(new Library("org.microg.nlp.api", "microG UnifiedNlp Api", "Apache License 2.0, by microG Team"));
libraries.add(new Library("org.microg.wearable", "microG Wearable", "Apache License 2.0 by microG Team"));
libraries.add(new Library("de.hdodenhof.circleimageview", "CircleImageView", "Apache License 2.0 by Henning Dodenhof"));
libraries.add(new Library("org.oscim.android", "<vector<tile>>map", "GNU LGPLv3 by Hannes Janetzek"));
libraries.add(new Library("com.squareup.wire", "Wire Protocol Buffers", "Apache License 2.0 by Square Inc."));
}
}
}

View File

@ -16,6 +16,7 @@
package org.microg.gms.wearable;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
@ -67,6 +68,8 @@ import java.util.Set;
import okio.ByteString;
import static android.os.Build.VERSION.SDK_INT;
public class WearableImpl {
private static final String TAG = "GmsWear";
@ -456,16 +459,7 @@ public class WearableImpl {
return null;
}
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, firstSignature, fixHost(uri.getHost(), false), uri.getPath());
int j = 0;
while (dataHolderItems.moveToNext()) {
for (int i = 0; i < dataHolderItems.getColumnCount(); i++) {
if (dataHolderItems.getType(i) == Cursor.FIELD_TYPE_STRING) {
Log.d(TAG, "getDataItems[" + j + "]: " + dataHolderItems.getColumnName(i) + "=" + dataHolderItems.getString(i));
}
if (dataHolderItems.getType(i) == Cursor.FIELD_TYPE_INTEGER)
Log.d(TAG, "getDataItems[" + j + "]: " + dataHolderItems.getColumnName(i) + "=" + dataHolderItems.getLong(i));
}
}
maybeDebugCursor("getDataItems",dataHolderItems);
dataHolderItems.moveToFirst();
dataHolderItems.moveToPrevious();
DataHolder dataHolder = new DataHolder(dataHolderItems, 0, null);
@ -473,6 +467,22 @@ public class WearableImpl {
return dataHolder;
}
@TargetApi(11)
private void maybeDebugCursor(String what, Cursor cursor) {
if (SDK_INT >= 11) {
int j = 0;
while (cursor.moveToNext()) {
for (int i = 0; i < cursor.getColumnCount(); i++) {
if (cursor.getType(i) == Cursor.FIELD_TYPE_STRING) {
Log.d(TAG, what+"[" + j + "]: " + cursor.getColumnName(i) + "=" + cursor.getString(i));
}
if (cursor.getType(i) == Cursor.FIELD_TYPE_INTEGER)
Log.d(TAG, what+"[" + j + "]: " + cursor.getColumnName(i) + "=" + cursor.getLong(i));
}
}
}
}
public synchronized void addListener(String packageName, IWearableListener listener) {
if (!listeners.containsKey(packageName)) {
listeners.put(packageName, new ArrayList<IWearableListener>());

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#000000"
android:pathData="M10,17.25V14H3V10H10V6.75L15.25,12L10,17.25M8,2H17A2,2 0 0,1 19,4V20A2,2 0 0,1 17,22H8A2,2 0 0,1 6,20V16H8V20H17V4H8V8H6V4A2,2 0 0,1 8,2Z"/>
</vector>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#000000"
android:pathData="M 12 4 C 9.11 4 6.5996094 5.6392969 5.3496094 8.0292969 C 2.3396094 8.3592969 0 10.9 0 14 A 6 6 0 0 0 6 20 L 19 20 A 5 5 0 0 0 24 15 C 24 12.36 21.949609 10.219297 19.349609 10.029297 C 18.669609 6.5892969 15.64 4 12 4 z M 12 7.5 A 0.5 0.5 0 0 1 12.5 8 L 12.5 8.5390625 C 13.92 8.7790625 15 10.015 15 11.5 L 15 14.5 L 16.5 16 L 7.5 16 L 9 14.5 L 9 11.5 C 9 10.015 10.08 8.7790625 11.5 8.5390625 L 11.5 8 A 0.5 0.5 0 0 1 12 7.5 z M 11 16.5 L 13 16.5 A 1 1 0 0 1 12 17.5 A 1 1 0 0 1 11 16.5 z"/>
</vector>

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
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"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clipChildren="false"
android:clipToPadding="false">
<LinearLayout
android:id="@+id/dialog_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:id="@+id/desc_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="16dip"
android:paddingLeft="20dip"
android:paddingRight="16dip"
android:paddingStart="20dip"
android:paddingTop="18dip">
<LinearLayout
android:id="@+id/perm_desc_root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/permission_icon"
android:layout_width="36dip"
android:layout_height="36dip"
android:scaleType="fitCenter"
android:src="@drawable/gcm_bell"
android:tint="?attr/colorAccent">
</ImageView>
<TextView
android:id="@+id/permission_message"
style="@style/TextAppearance.AppCompat.Subhead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="16dip"
android:paddingStart="16dip"
android:text="Allow {appName} to register for push notifications?"
android:textSize="20sp">
</TextView>
</LinearLayout>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingEnd="16dip"
android:paddingLeft="20dip"
android:paddingRight="16dip"
android:paddingStart="20dip">
<android.support.v7.widget.ButtonBarLayout
android:id="@+id/button_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:orientation="horizontal"
android:paddingBottom="4dp"
android:paddingLeft="6dip"
android:paddingStart="6dip"
android:paddingTop="4dp">
<TextView
android:id="@+id/current_page_text"
style="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:maxLines="1"
android:paddingEnd="12dp"
android:paddingRight="12dp"
android:textColor="?android:attr/textColorSecondary"
android:visibility="invisible">
</TextView>
<android.support.v4.widget.Space
android:id="@*android:id/spacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="invisible">
</android.support.v4.widget.Space>
<Button
android:id="@+id/permission_deny_button"
style="?attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/deny">
</Button>
<Button
android:id="@+id/permission_allow_button"
style="?attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/allow">
</Button>
</android.support.v7.widget.ButtonBarLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -118,14 +118,14 @@
<Button
android:id="@android:id/button2"
style="?android:attr/buttonBarButtonStyle"
style="?attr/buttonBarButtonStyle"
android:text="@string/deny"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@android:id/button1"
style="?android:attr/buttonBarButtonStyle"
style="?attr/buttonBarButtonStyle"
android:text="@string/allow"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

View File

@ -99,7 +99,7 @@
<Button
android:id="@android:id/button1"
style="?android:attr/buttonBarButtonStyle"
style="?attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@android:string/ok"/>

View File

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 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:minHeight="?attr/listPreferredItemHeight"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
android:paddingRight="?attr/listPreferredItemPaddingRight">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:maxHeight="40dp"
android:maxWidth="40dp"
android:minHeight="40dp"
android:minWidth="40dp"
android:scaleType="fitCenter"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:minHeight="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:ellipsize="end"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="@android:color/darker_gray"/>
<TextView
android:id="@+id/warning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textColor="@color/material_error"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 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.
-->
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_view"
android:layout_height="match_parent"
android:layout_width="match_parent">
</ListView>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2013 The Android Open Source Project
~ Copyright (C) 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.
-->
<!-- text that appears when the recent app list is empty -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingEnd="?android:attr/scrollbarSize"
android:background="?attr/selectableItemBackground"
android:paddingRight="?android:attr/scrollbarSize">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dip"
android:layout_marginEnd="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1"
android:layout_marginLeft="15dip"
android:layout_marginRight="6dip">
<TextView android:id="@+android:id/title"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" />
</RelativeLayout>
</LinearLayout>

View File

@ -70,7 +70,7 @@
android:layout_gravity="top"
android:layout_marginTop="12.0dip"
android:ellipsize="end"
android:singleLine="true"
android:maxLines="1"
android:text="@string/place_picker_select_title"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?attr/colorAccent"/>

View File

@ -25,10 +25,10 @@
<string name="sorry">Entschuldigung…</string>
<string name="no_network_error_desc">"Du hast keine Netzwerkverbindung.
Dies könnte ein temporäres Problem sein oder dein Android Gerät hat keine bestehende Datenverbindung. Versuche es nocheinmal wenn du mit einem Datennetz oder WLAN verbunden bist."</string>
Dies könnte ein temporäres Problem sein oder dein Android Gerät hat keine bestehende Datenverbindung. Versuche es noch einmal wenn du mit einem Datennetz oder WLAN verbunden bist."</string>
<string name="auth_general_error_desc">"Es ist ein Problem bei der Kommunikation mit den Google Servern aufgetreten.
Versuche es später nocheinmal."</string>
Versuche es später noch einmal."</string>
<string name="auth_finalize">"Dein Gerät verbindet sich zu Google um Informationen in deinem Konto zu speichern.
Dies kann einige Minuten dauern."</string>
@ -43,10 +43,8 @@ Dies kann einige Minuten dauern."</string>
<string name="pref_auth_trust_google_title">Vertraue Google bei App Rechten</string>
<string name="pref_auth_trust_google_summary">Wenn deaktiviert, wird der Nutzer gefragt bevor eine App Autorisierungsanfrage an Google geschickt wird. Einige Apps werden das Google Konto nicht nutzen können, wenn deaktiviert.</string>
<string name="pref_checkin_enable_title">Aktiviere Geräte Check-In</string>
<string name="pref_checkin_enable_summary">Geräte Check-In ist ein versteckter Prozess, der genutzt wird um eine eindeutige Kennung für Google Dienste zu erzeugen. microG entfernt identifizierende Daten bis auf den Google Konto Namen.</string>
<string name="pref_gcm_enable_mcs_title">Aktiviere Google Cloud Messaging</string>
<string name="pref_gcm_enable_mcs_summary">Google Cloud Messaging ist ein Push-Nachrichten Dienst, der von vielen Apps genutzt wird. Zur Benutztung muss Geräte Check-In aktiviert werden.</string>
<string name="prefcat_services">Hintergrunddienste</string>

View File

@ -47,10 +47,8 @@ Esto podría tardar algunos minutos."</string>
<string name="prefcat_services">Servicios de segundo plano</string>
<string name="pref_checkin_enable_title">Habilitar registro del dispositivo</string>
<string name="pref_checkin_enable_summary">El registro del dispositivo es un proceso oculto que se usa para crear un identificador único para los servicios de Google. microG quita información identificativa aparte del nombre en tu cuenta de Google de estos datos.</string>
<string name="pref_gcm_enable_mcs_title">Habilitar Google Cloud Messaging</string>
<string name="pref_gcm_enable_mcs_summary">Google Cloud Messaging es un proveedor de notificaciones push usado por muchas aplicaciones. Para usarlo debes habilitar el registro del dispositivo.</string>
<string name="pref_gcm_heartbeat_title">Intervalo del Cloud Messaging heartbeat</string>

View File

@ -44,9 +44,7 @@ To zajmie kilka minut."</string>
<string name="pref_auth_trust_google_title">Zaufaj uprawnieniom aplikacji Google</string>
<string name="pref_auth_trust_google_summary">Po wyłączeniu, do użytkownika kierowane jest pytanie przed wysyłaniem prośby o autoryzację do Google. Niektóre aplikacje nie będą mogły skorzystać z konta Google po wyłączeniu tej opcji.</string>
<string name="pref_checkin_enable_title">Uruchom zgłaszanie urządzenia</string>
<string name="pref_checkin_enable_summary">Zgłaszanie urządzenia to ukryty proces, służący do wygenerowania unikalnego identyfikatora dla usług Google.Usługi microG wyciągają inne dane identyfikacyjne niż nazwa Twojego konta z tego identyfikatora.</string>
<string name="pref_gcm_enable_mcs_title">Uruchom Google Cloud Messaging</string>
<string name="pref_gcm_enable_mcs_summary">Google Cloud Messaging jest systemem zapewniającym powiadomienia dla wielu aplikacji. Żeby z niego korzystać, musisz uruchomić zgłaszanie urządzenia.</string>
<string name="prefcat_services">Usługi w tle</string>

View File

@ -41,9 +41,7 @@ Acest lucru poate dura câteva minute."</string>
<string name="perm_c2dm_send_label">trimite mesaje C2DM altor aplicaţii</string>
<string name="pref_auth_trust_google_title">Credeţi Google pentru permisiunile aplicaţiei</string>
<string name="pref_auth_trust_google_summary">Dacă este dezactivat, utilizatorul este întrebat înainte de trimiterea către Google a unei solicitări de autorizare a aplicaţiei. Unele aplicații nu vor reuși să utilizeze contul Google, dacă această opţiune este dezactivată.</string>
<string name="pref_checkin_enable_title">Activaţi înregistrarea dispozitivului</string>
<string name="pref_checkin_enable_summary">\"Înregistrarea dispozitivului\" este un proces ascuns care este utilizat pentru a crea un identificator unic pentru serviciile Google. Serviciile microG înlătură alte date de identificare cu excepţia numelui contului dvs. Google.</string>
<string name="pref_gcm_enable_mcs_title">Activaţi Mesageria Google Cloud</string>
<string name="pref_gcm_enable_mcs_summary">Mesageria Google Cloud este un furnizor de notificări de ti push utilizat de multe aplicații. Pentru a utiliza trebuie să activați \"Înregistrarea dispozitivului\".</string>
<string name="prefcat_services">Servicii de fundal</string>
<string name="prefcat_components">Componente</string>

View File

@ -48,10 +48,8 @@
<string name="prefcat_services">Услуге у позадини</string>
<string name="pref_checkin_enable_title">Укључи пријаву уређаја</string>
<string name="pref_checkin_enable_summary">Пријава уређаја је скривени процес који служи за прављење јединственог идентификатора за Гуглове услуге. микроГ сервиси скидају идентификујуће бите са ових података, осим имена вашег Гугл налога.</string>
<string name="pref_gcm_enable_mcs_title">Омогући Гуглове облак поруке</string>
<string name="pref_gcm_enable_mcs_summary">Гуглове облак поруке (Google Cloud Messaging) је провајдер гурања обавештења (push) којег користе многе апликације. Да бисте га користили укључите пријаву уређаја.</string>
<string name="pref_gcm_heartbeat_title">Интервал откуцаја облак порука</string>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 microG Project Team
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 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.
@ -14,7 +15,18 @@
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/delete"
android:title="@string/gcm_unregister_app" />
</menu>
<resources>
<string-array name="network_types" translatable="false">
<item>mobile</item>
<item>wifi</item>
<item>roaming</item>
<item>other</item>
</string-array>
<string-array name="network_types_names">
<item>@string/network_type_mobile</item>
<item>@string/network_type_wifi</item>
<item>@string/network_type_roaming</item>
<item>@string/network_type_other</item>
</string-array>
</resources>

View File

@ -14,17 +14,15 @@
~ limitations under the License.
-->
<resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="gms_app_name">microG Services Core</string>
<string name="gms_settings_name">microG Settings</string>
<string name="google_account_type" translatable="false">com.google</string>
<string name="just_a_sec">Just a sec…</string>
<string name="google_account_label">Google</string>
<string name="ask_permission_tos">By continuing, you allow this app and Google to use your information in accordance with their respective terms of service and privacy policies.</string>
<string name="ask_scope_permission_title">%1$s would like to:</string>
<string name="ask_service_permission_title">%1$s would like to use:</string>
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> would like to:</string>
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> would like to use:</string>
<string name="account_manager_title">Google Account Manager</string>
<string name="sorry">Sorry…</string>
<string name="no_network_error_desc">"You don't have a network connection.
@ -39,31 +37,30 @@ This can take a couple of minutes."</string>
<string name="allow">Allow</string>
<string name="deny">Deny</string>
<string name="auth_notification_title">Authentication required</string>
<string name="auth_notification_content">%1$s requires your authorization to access your Google account.</string>
<string name="auth_notification_content"><xliff:g example="F-Droid">%1$s</xliff:g> requires your authorization to access your Google account.</string>
<string name="perm_status_broadcast_label">listen to internal status broadcasts</string>
<string name="perm_c2dm_receive_label">listen to C2DM messages</string>
<string name="perm_c2dm_send_label">send C2DM messages to other apps</string>
<string name="pref_auth_trust_google" translatable="false">auth_manager_trust_google</string>
<string name="pref_auth_trust_google_title">Trust Google for app permissions</string>
<string name="pref_auth_trust_google_summary">When disabled, the user is asked before an apps authorization request is sent to Google. Some applications will fail to use the Google account if this is disabled.</string>
<string name="prefcat_services">Background services</string>
<string name="prefs_account">Account preferences</string>
<string name="prefs_account_privacy">Personal info &amp; privacy</string>
<string name="prefs_account_security">Sign-in &amp; security</string>
<string name="pref_checkin_enable" translatable="false">checkin_enable_service</string>
<string name="pref_checkin_enable_title">Enable device checkin</string>
<string name="pref_checkin_enable_summary">Device checkin is a hidden process that is used to create an unique identifier for Google services. microG Services strips identifying bits other than your Google account name from this data.</string>
<string name="prefcat_services">Services</string>
<string name="pref_gcm_enable_mcs" translatable="false">gcm_enable_mcs_service</string>
<string name="pref_gcm_enable_mcs_title">Enable Google Cloud Messaging</string>
<string name="pref_gcm_enable_mcs_summary">Google Cloud Messaging is a push notification provider used by many applications. To use it you must enable device checkin.</string>
<string name="service_name_checkin">Google device registration</string>
<string name="service_name_mcs">Google Cloud Messaging</string>
<string name="pref_checkin_enable_summary">Registers your device to Google services and creates a unique device identifier. microG strips identifying bits other than your Google account name from registration data.</string>
<string name="pref_gcm_enable_mcs_summary">Google Cloud Messaging is a push notification provider used by many third-party applications. To use it you must enable device registration.</string>
<string name="pref_gcm_heartbeat" translatable="false">gcm_heartbeat_interval</string>
<string name="pref_gcm_heartbeat_title">Cloud Messaging heartbeat interval</string>
<string name="pref_gcm_heartbeat_summary">The interval in seconds for the system to heartbeat the Google servers. Increasing this number will reduce battery consumption, but might cause delays on push messages.</string>
<string name="pref_gcm_heartbeat_summary">The interval in seconds for the system to heartbeat the Google servers. Increasing this number will reduce battery consumption, but might cause delays on push messages.\nDeprecated, will be replaced in future release.</string>
<string name="pref_gcm_apps">gcm_apps</string>
<string name="pref_gcm_apps_title">Apps using Google Cloud Messaging</string>
<string name="pref_gcm_apps_summary">List of apps currently registered for Google Cloud Messaging.</string>
@ -71,7 +68,7 @@ This can take a couple of minutes."</string>
<string name="prefcat_location_service">Location service</string>
<string name="games_title">Google Play Games</string>
<string name="games_info_title">%1$s would like to use Play Games</string>
<string name="games_info_title"><xliff:g example="F-Droid">%1$s</xliff:g> would like to use Play Games</string>
<string name="games_info_content">To use Play Games it is required to install the Google Play Games app. The application might continue without Play Games, but it is possible that it will behave unexpectedly.</string>
<string name="pick_place_title">Pick a place</string>
@ -93,25 +90,52 @@ This can take a couple of minutes."</string>
<string name="self_check_pkg_gms">Play Services (GmsCore)</string>
<string name="self_check_pkg_vending">Play Store (Phonesky)</string>
<string name="self_check_pkg_gsf">Services Framework (GSF)</string>
<string name="self_check_name_app_installed">%1$s installed: </string>
<string name="self_check_resolution_app_installed">Install the application %1$s or a compatible one. Please check the documentation on which applications are compatible.</string>
<string name="self_check_name_correct_sig">%1$s has correct signature: </string>
<string name="self_check_resolution_correct_sig">Either the installed %1$s is not compatible or signature spoofing is not active for it. Please check the documentation on which applications and ROMs are compatible.</string>
<string name="self_check_name_app_installed"><xliff:g example="F-Droid">%1$s</xliff:g> installed: </string>
<string name="self_check_resolution_app_installed">Install the application <xliff:g example="F-Droid">%1$s</xliff:g> or a compatible one. Please check the documentation on which applications are compatible.</string>
<string name="self_check_name_correct_sig"><xliff:g example="F-Droid">%1$s</xliff:g> has correct signature: </string>
<string name="self_check_resolution_correct_sig">Either the installed <xliff:g example="F-Droid">%1$s</xliff:g> is not compatible or signature spoofing is not active for it. Please check the documentation on which applications and ROMs are compatible.</string>
<string name="perm_extended_access_label">Extended access to Google services</string>
<string name="self_check_cat_system">System</string>
<string name="self_check_name_battery_optimizations">Battery optimizations ignored:</string>
<string name="self_check_resolution_battery_optimizations">Touch here to disable battery optimizations. Not doing this may result in misbehaving applications</string>
<string name="lacking_permission_toast">microG Services Core: Lacking permission %1$s</string>
<string name="lacking_permission_toast">microG Services Core: Lacking permission to <xliff:g example="have full network acccess">%1$s</xliff:g></string>
<string name="place_picker_select_title">Select this location</string>
<string name="place_picker_nearby_places">Nearby places</string>
<string name="place_picker_location_lat_lng">(%1$.7f, %2$.7f)</string>
<string name="gcm_registered_apps_counter"><xliff:g example="123">%1$i</xliff:g> registered Apps</string>
<string name="gcm_app_error_unregistering">Error unregistering</string>
<string name="gcm_app_not_installed_anymore">No longer installed</string>
<string name="gcm_messages_received_no">%1$s messages received</string>
<string name="gcm_messages_received_no"><xliff:g example="123">%1$i</xliff:g> messages received</string>
<string name="gcm_unregister_app">Unregister</string>
<string name="gcm_not_registered">Not registered</string>
<string name="gcm_no_message_yet">No messages received so far</string>
<string name="gcm_last_message_at">Last message: <xliff:g example="Yesterday, 02:20 PM">%1$s</xliff:g></string>
<string name="gcm_registered">Registered</string>
<string name="gcm_registered_since">Registered since: <xliff:g example="Yesterday, 02:20 PM">%1$s</xliff:g></string>
<string name="gcm_unregister_confirm_title">Unregister <xliff:g example="F-Droid">%1$s</xliff:g>?</string>
<string name="gcm_unregister_confirm_message">Some apps do not automatically re-register and/or do not provide an option to do so manually. These apps might not work correctly after unregistering.\nContinue?</string>
<string name="gcm_messages_counter">Messages: <xliff:g example="123">%1$i</xliff:g> (<xliff:g example="12345">%2$i</xliff:g> bytes)</string>
<string name="gcm_state_disconnected">Current State: Disconnected</string>
<string name="gcm_state_connected">Current State: Connected since <xliff:g example="2 hours ago">%1$s</xliff:g></string>
<string name="menu_advanced">Advanced</string>
<string name="list_no_item_none">None</string>
<string name="cond_gcm_bat_title">Battery optimizations enabled</string>
<string name="cond_gcm_bat_summary">You enabled Google Cloud Messaging but have battery optimizations active for microG Services Core. For push notifications to arrive you should ignore battery optimizations.</string>
<string name="cond_gcm_bat_action">Ignore optimizations</string>
<string name="cond_perm_title">Permission missing</string>
<string name="cond_perm_summary">One or more permissions required for proper functionality of microG Service Core is missing.</string>
<string name="cond_perm_action">Request missing permissions</string>
<string name="network_type_mobile">Mobile network</string>
<string name="network_type_wifi">Wi-Fi</string>
<string name="network_type_roaming">Roaming</string>
<string name="network_type_other">Other networks</string>
</resources>

View File

@ -15,8 +15,8 @@
-->
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountPreferences="@xml/account_preferences"
android:accountType="@string/google_account_type"
android:accountPreferences="@xml/preferences_auth"
android:accountType="com.google"
android:customTokens="true"
android:icon="@drawable/proprietary_auth_gls_ic_google_selected"
android:smallIcon="@drawable/proprietary_auth_gls_ic_google_minitab_selected"

View File

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/prefcat_setup">
<Preference
android:key="@string/self_check_title"
android:summary="@string/self_check_desc"
android:title="@string/self_check_title">
</Preference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/prefcat_services">
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/pref_checkin_enable"
android:summary="@string/pref_checkin_enable_summary"
android:title="@string/pref_checkin_enable_title"/>
<CheckBoxPreference
android:defaultValue="false"
android:dependency="@string/pref_checkin_enable"
android:key="@string/pref_gcm_enable_mcs"
android:summary="@string/pref_gcm_enable_mcs_summary"
android:title="@string/pref_gcm_enable_mcs_title"/>
<EditTextPreference
android:defaultValue="300"
android:dependency="@string/pref_gcm_enable_mcs"
android:key="@string/pref_gcm_heartbeat"
android:summary="@string/pref_gcm_heartbeat_summary"
android:title="@string/pref_gcm_heartbeat_title"/>
<Preference
android:dependency="@string/pref_gcm_enable_mcs"
android:key="@string/pref_gcm_apps"
android:summary="@string/pref_gcm_apps_summary"
android:title="@string/pref_gcm_apps_title">
</Preference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/prefcat_location_service">
<Preference android:title="@string/nlp_settings_label">
<intent
android:targetClass="org.microg.nlp.ui.SettingsActivity"
android:targetPackage="com.google.android.gms"/>
</Preference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/prefcat_about">
<Preference
android:key="@string/pref_about_title"
android:summary="@string/pref_about_summary"
android:title="@string/pref_about_title"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 microG Project Team
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 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.
@ -15,11 +16,16 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/account_manager_title">
<CheckBoxPreference
android:key="@string/pref_auth_trust_google"
<SwitchPreference
android:defaultValue="true"
android:title="@string/pref_auth_trust_google_title"
android:summary="@string/pref_auth_trust_google_summary" />
android:key="auth_manager_trust_google"
android:summary="@string/pref_auth_trust_google_summary"
android:title="@string/pref_auth_trust_google_title"/>
</PreferenceCategory>
</PreferenceScreen>
<Preference
android:selectable="false"
android:summary="Google account settings not yet implemented."/>
</PreferenceScreen>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:key="k2"
android:title="@string/prefs_account_security">
<intent android:action="com.google.android.gms.accountsettings.SECURITY_SETTINGS"/>
</PreferenceScreen>
<PreferenceScreen
android:key="k3"
android:title="@string/prefs_account_privacy">
<intent android:action="com.google.android.gms.accountsettings.PRIVACY_SETTINGS"/>
</PreferenceScreen>
<PreferenceScreen
android:key="k4"
android:title="@string/prefs_account">
<intent android:action="com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS"/>
</PreferenceScreen>
</PreferenceScreen>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:selectable="false"
android:summary="@string/pref_gcm_enable_mcs_summary"/>
<Preference
android:key="pref_gcm_status"
android:persistent="false"
android:selectable="false"
android:summary="Current state: Connected since 2 min ago"/>
<PreferenceCategory
android:key="gcm_apps"
android:title="@string/pref_gcm_apps_title"/>
</PreferenceScreen>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:defaultValue="300"
android:key="gcm_heartbeat_interval"
android:summary="@string/pref_gcm_heartbeat_summary"
android:title="@string/pref_gcm_heartbeat_title"/>
<SwitchPreference
android:defaultValue="false"
android:key="gcm_confirm_new_apps"
android:summary="Ask before registering a new app to receive push notifications"
android:title="Confirm new apps"/>
<MultiSelectListPreference
android:enabled="false"
android:entries="@array/network_types_names"
android:entryValues="@array/network_types"
android:key="gcm_network_types"
android:summary="Connect to Google servers while connected to one of these networks.\nNot yet supported"
android:title="Networks"/>
<SwitchPreference
android:defaultValue="false"
android:enabled="false"
android:key="gcm_use_tor"
android:summary="Requires Orbot to be installed\nNot yet supported"
android:title="Connect via Tor network"/>
<PreferenceCategory
android:enabled="false"
android:title="Connection configuration\nNot yet supported">
<ListPreference
android:defaultValue="auto"
android:key="gcm_ping_mobile"
android:summary="Automatic: 13 min"
android:title="Mobile network heartbeat interval"/>
<ListPreference
android:defaultValue="auto"
android:key="gcm_ping_wifi"
android:summary="Automatic: 30 min"
android:title="Wi-Fi heartbeat interval"/>
<ListPreference
android:defaultValue="auto"
android:key="gcm_ping_other"
android:summary="Automatic: 30 min"
android:title="Other network heartbeat interval"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="Messaging">
<SwitchPreference
android:defaultValue="true"
android:key="gcm_app_wake_for_delivery"
android:persistent="false"
android:summary="Start the app in background to receive the incoming push messages. Disabling this might delay message delivery up to the moment the app is started or cause complete message loss."
android:title="Start app on push message"/>
<Preference
android:selectable="false"
android:key="gcm_app_message_details"
android:summary="Messages: 1000 (37 MB)\nLast message: 22 min ago"/>
</PreferenceCategory>
<PreferenceCategory
android:title="Registration">
<SwitchPreference
android:defaultValue="true"
android:key="gcm_app_allow_register"
android:persistent="false"
android:summary="Allow the app to register for push notifications."
android:title="Allow registration"/>
<Preference
android:title="Unregister"
android:key="gcm_app_register_details"
android:summary="Registered since: 3 month ago"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/prefcat_setup">
<org.microg.tools.ui.TintIconPreference
android:icon="@drawable/self_check"
android:summary="@string/self_check_desc"
android:title="@string/self_check_title">
<intent
android:targetClass="org.microg.gms.ui.SelfCheckFragment$AsActivity"
android:targetPackage="com.google.android.gms"/>
</org.microg.tools.ui.TintIconPreference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/prefcat_services">
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/device_login"
android:key="checkin_enable_service"
android:summary="@string/pref_checkin_enable_summary"
android:title="@string/service_name_checkin"/>
<org.microg.tools.ui.TintIconPreference
android:dependency="checkin_enable_service"
android:icon="@drawable/gcm_bell"
android:key="pref_gcm"
android:title="@string/service_name_mcs">
<intent
android:targetClass="org.microg.gms.ui.GcmFragment$AsActivity"
android:targetPackage="com.google.android.gms"/>
</org.microg.tools.ui.TintIconPreference>
<org.microg.tools.ui.TintIconPreference
android:icon="@drawable/location_marker"
android:title="@string/nlp_settings_label">
<intent
android:targetClass="org.microg.nlp.ui.SettingsActivity"
android:targetPackage="com.google.android.gms"/>
</org.microg.tools.ui.TintIconPreference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/prefcat_about">
<org.microg.tools.ui.TintIconPreference
android:icon="@drawable/info"
android:key="pref_about"
android:title="@string/pref_about_title">
<intent
android:targetClass="org.microg.gms.ui.AboutFragment$AsActivity"
android:targetPackage="com.google.android.gms"/>
</org.microg.tools.ui.TintIconPreference>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -10,6 +10,7 @@ include ':play-services-basement'
include ':play-services-api'
include ':play-services-cast-api'
include ':play-services-cast-framework-api'
include ':play-services-iid-api'
include ':play-services-location-api'
include ':play-services-wearable-api'