From 33a6137aecb2ff24793eea0494fbbc000ccc86d6 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Sun, 28 Aug 2016 13:01:35 +0200 Subject: [PATCH] Screen showing GCM registered apps + infos + unregistration option --- .../main/java/org/microg/gms/gcm/GcmData.java | 130 +++++++++++++++ .../java/org/microg/gms/gcm/McsService.java | 4 + .../microg/gms/gcm/PushRegisterService.java | 39 +++-- .../gms/ui/GcmRegisteredAppsFragment.java | 148 ++++++++++++++++++ .../org/microg/gms/ui/SettingsActivity.java | 12 ++ .../src/main/res/layout/gcm_app.xml | 69 ++++++++ .../src/main/res/layout/gcm_apps_list.xml | 21 +++ .../src/main/res/menu/gcm_app.xml | 20 +++ .../src/main/res/values/strings.xml | 9 ++ .../src/main/res/xml/gms_preferences.xml | 6 + 10 files changed, 443 insertions(+), 15 deletions(-) create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/GcmData.java create mode 100644 play-services-core/src/main/java/org/microg/gms/ui/GcmRegisteredAppsFragment.java create mode 100644 play-services-core/src/main/res/layout/gcm_app.xml create mode 100644 play-services-core/src/main/res/layout/gcm_apps_list.xml create mode 100644 play-services-core/src/main/res/menu/gcm_app.xml diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmData.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmData.java new file mode 100644 index 00000000..43130b84 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/GcmData.java @@ -0,0 +1,130 @@ +/* + * 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.gcm; + +import android.content.Context; +import android.content.SharedPreferences; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class GcmData { + 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%%"; + + private Context context; + + public GcmData(Context context) { + this.context = context; + } + + public static class AppInfo implements Comparable { + public String app = null; + public String appSignature = null; + public String registerID = null; + + private final int STATE_ERROR = 1; + private final int STATE_REMOVED = 2; + private final int STATE_REGISTERED = 3; + private int state; + + public AppInfo(String key, String value) { + if (ERROR.equals(value)) { + state = STATE_ERROR; + } else if (REMOVED.equals(value)) { + state = STATE_REMOVED; + } else { + state = STATE_REGISTERED; + registerID = value; + } + String[] splitKey = key.split(":"); + app = splitKey[0]; + appSignature = splitKey[1]; + } + + public boolean isRegistered() { + return state == STATE_REGISTERED; + } + + public boolean isRemoved() { + return state == STATE_REMOVED; + } + + public boolean hasUnregistrationError() { + return state == STATE_ERROR; + } + + @Override + public int compareTo(AppInfo another) { + return app.compareTo(another.app); + } + } + + public void app_registered(String app, String signature, String regId) { + getInfoSharedPreferences().edit().putString(app + ":" + signature, regId).apply(); + } + + public void app_registration_error(String app, String signature) { + getInfoSharedPreferences().edit().putString(app + ":" + signature, "-").apply(); + } + + public void app_unregistered(String app, String signature) { + getInfoSharedPreferences().edit().putString(app + ":" + signature, REMOVED).apply(); + } + + public void app_unregistration_error(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(); + } + + public int getAppMessageCount(String app) { + return getStatsSharedPreferences().getInt(app, 0); + } + + public AppInfo getAppInfo(String app, String signature) { + return getAppInfo(app + signature); + } + + public List getAppsInfo() { + ArrayList ret = new ArrayList<>(); + Set keys = getInfoSharedPreferences().getAll().keySet(); + for (String key : keys) { + ret.add(getAppInfo(key)); + } + return ret; + } + + private AppInfo getAppInfo(String key) { + return new AppInfo(key, getInfoSharedPreferences().getString(key, "")); + } + + private SharedPreferences getInfoSharedPreferences() { + return context.getSharedPreferences(GCM_REGISTRATION_PREF, Context.MODE_PRIVATE); + } + + private SharedPreferences getStatsSharedPreferences() { + return context.getSharedPreferences(GCM_MESSAGES_PREF, Context.MODE_PRIVATE); + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java index 373e8535..800fb7f4 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java @@ -100,6 +100,8 @@ public class McsService extends Service implements Handler.Callback { private static HandlerThread handlerThread; private static Handler rootHandler; + private GcmData gcmStorage = new GcmData(this); + private AlarmManager alarmManager; private PowerManager powerManager; private static PowerManager.WakeLock wakeLock; @@ -322,6 +324,8 @@ public class McsService extends Service implements Handler.Callback { } if (infos.isEmpty()) logd("No target for message, wut?"); + + gcmStorage.incrementAppMessageCount(msg.category, 1); } private void handleSelfMessage(DataMessageStanza msg) { diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java index 470d5bf8..9e5d8633 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java @@ -20,7 +20,6 @@ import android.app.IntentService; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Build; import android.os.Message; import android.os.Messenger; @@ -48,20 +47,34 @@ import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED; public class PushRegisterService extends IntentService { private static final String TAG = "GmsGcmRegisterSvc"; - - private static final String REMOVED = "%%REMOVED%%"; - private static final String ERROR = "%%ERROR%%"; - private static final String GCM_REGISTRATION_PREF = "gcm_registrations"; - private static final String EXTRA_SKIP_TRY_CHECKIN = "skip_checkin"; + private GcmData gcmStorage = new GcmData(this); + public PushRegisterService() { super(TAG); setIntentRedelivery(false); } - private SharedPreferences getSharedPreferences() { - return getSharedPreferences(GCM_REGISTRATION_PREF, MODE_PRIVATE); + public static RegisterResponse register(Context context, String app, String appSignature, String sender, String info) { + RegisterResponse response = register(context, app, appSignature, sender, info, false); + String regId = response.token; + if (regId != null) { + (new GcmData(context)).app_registered(app, appSignature, regId); + } else { + (new GcmData(context)).app_registration_error(app, appSignature); + } + 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)).app_unregistration_error(app, appSignature); + } else { + (new GcmData(context)).app_unregistered(app, appSignature); + } + return response; } @Override @@ -106,13 +119,11 @@ public class PushRegisterService extends IntentService { Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); String appSignature = PackageUtils.firstSignatureDigest(this, app); - String regId = register(this, app, appSignature, sender, null, false).token; + String regId = register(this, app, appSignature, sender, null).token; if (regId != null) { outIntent.putExtra(EXTRA_REGISTRATION_ID, regId); - getSharedPreferences().edit().putString(app + ":" + appSignature, regId).apply(); } else { outIntent.putExtra(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE); - getSharedPreferences().edit().putString(app + ":" + appSignature, "-").apply(); } Log.d(TAG, "register[res]: " + outIntent + " extras=" + outIntent.getExtras()); @@ -159,20 +170,18 @@ public class PushRegisterService extends IntentService { Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); String appSignature = PackageUtils.firstSignatureDigest(this, app); - if (REMOVED.equals(getSharedPreferences().getString(app + ":" + appSignature, null))) { + if (!gcmStorage.getAppInfo(app, appSignature).isRemoved()) { outIntent.putExtra(EXTRA_UNREGISTERED, app); } else { - RegisterResponse response = register(this, app, appSignature, null, null, true); + RegisterResponse response = unregister(this, app, appSignature, null, null); if (!app.equals(response.deleted)) { outIntent.putExtra(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE); - getSharedPreferences().edit().putString(app + ":" + PackageUtils.firstSignatureDigest(this, app), ERROR).apply(); if (response.retryAfter != null && !response.retryAfter.contains(":")) { outIntent.putExtra(EXTRA_RETRY_AFTER, Long.parseLong(response.retryAfter)); } } else { outIntent.putExtra(EXTRA_UNREGISTERED, app); - getSharedPreferences().edit().putString(app + ":" + PackageUtils.firstSignatureDigest(this, app), REMOVED).apply(); } } diff --git a/play-services-core/src/main/java/org/microg/gms/ui/GcmRegisteredAppsFragment.java b/play-services-core/src/main/java/org/microg/gms/ui/GcmRegisteredAppsFragment.java new file mode 100644 index 00000000..cbda8a72 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/ui/GcmRegisteredAppsFragment.java @@ -0,0 +1,148 @@ +/* + * 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); + listView.setAdapter(updateListView()); + return listView; + } + + @Override + public void onResume() { + super.onResume(); + ((ListView) getView().findViewById(R.id.list_view)).setAdapter(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() { + + @Override + protected Void doInBackground(Void... params) { + PushRegisterService.unregister(getContext(), appInfo.app, appInfo.appSignature, null, null); + return null; + } + + protected void onPostExecute(Void result) { + ((ListView) getView().findViewById(R.id.list_view)).setAdapter(updateListView()); + } + }.execute(); + return true; + } + + synchronized public AppsAdapter updateListView() { + ArrayList registeredApps = new ArrayList(); + for (GcmData.AppInfo appInfo : gcmStorage.getAppsInfo()) { + if (appInfo.isRegistered()) { + registeredApps.add(appInfo); + } + } + appsAdapter = new AppsAdapter(getContext(), registeredApps.toArray(new GcmData.AppInfo[registeredApps.size()])); + return appsAdapter; + } + + private class AppsAdapter extends ArrayAdapter { + + public AppsAdapter(Context context, GcmData.AppInfo[] libraries) { + super(context, R.layout.gcm_app, R.id.title, 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)); + } + } catch (PackageManager.NameNotFoundException e) { + title.setText(appInfo.app); + 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; + } + } +} \ No newline at end of file diff --git a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java b/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java index 135b1170..9098f250 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java @@ -87,6 +87,18 @@ public class SettingsActivity extends AppCompatActivity { 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; + } + }); } } diff --git a/play-services-core/src/main/res/layout/gcm_app.xml b/play-services-core/src/main/res/layout/gcm_app.xml new file mode 100644 index 00000000..eb46c8fe --- /dev/null +++ b/play-services-core/src/main/res/layout/gcm_app.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + diff --git a/play-services-core/src/main/res/layout/gcm_apps_list.xml b/play-services-core/src/main/res/layout/gcm_apps_list.xml new file mode 100644 index 00000000..a470a7c3 --- /dev/null +++ b/play-services-core/src/main/res/layout/gcm_apps_list.xml @@ -0,0 +1,21 @@ + + + + diff --git a/play-services-core/src/main/res/menu/gcm_app.xml b/play-services-core/src/main/res/menu/gcm_app.xml new file mode 100644 index 00000000..58e09fc9 --- /dev/null +++ b/play-services-core/src/main/res/menu/gcm_app.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/values/strings.xml b/play-services-core/src/main/res/values/strings.xml index e23cec18..7d01429d 100644 --- a/play-services-core/src/main/res/values/strings.xml +++ b/play-services-core/src/main/res/values/strings.xml @@ -63,6 +63,10 @@ This can take a couple of minutes." Cloud Messaging heartbeat interval 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. + gcm_apps + Apps using Google Cloud Messaging + List of apps currently registered for Google Cloud Messaging. + Components Location service @@ -101,4 +105,9 @@ This can take a couple of minutes." Nearby places (%1$.7f, %2$.7f) + Error unregistering + No longer installed + %1$s messages received + Unregister + diff --git a/play-services-core/src/main/res/xml/gms_preferences.xml b/play-services-core/src/main/res/xml/gms_preferences.xml index 71341a99..b2bde6be 100644 --- a/play-services-core/src/main/res/xml/gms_preferences.xml +++ b/play-services-core/src/main/res/xml/gms_preferences.xml @@ -40,6 +40,12 @@ android:key="@string/pref_gcm_heartbeat" android:summary="@string/pref_gcm_heartbeat_summary" android:title="@string/pref_gcm_heartbeat_title"/> + +