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

Commit 69144e94 authored by Steve Kondik's avatar Steve Kondik
Browse files

cmparts: Refactor for RemotePreference and SettingsHelper

 * Use the new stuff in the SDK to standardize on how to deal with
   remote components and settings.
 * Eliminates code and should be more secure.

Change-Id: I9fd148a844877004cd1526a5fc86d3d1529ecbc8
parent 754340cf
Loading
Loading
Loading
Loading
+13 −10
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />

    <uses-permission android:name="cyanogenmod.permission.MANAGE_PARTS" />
    <uses-permission android:name="cyanogenmod.permission.MANAGE_REMOTE_PREFERENCES" />

    <protected-broadcast android:name="cyanogenmod.platform.app.profiles.PROFILES_STATE_CHANGED" />
    <protected-broadcast android:name="org.cyanogenmod.cmparts.PART_CHANGED" />
@@ -49,23 +49,26 @@
            android:defaultToDeviceProtectedStorage="true"
            android:directBootAware="true">

        <activity android:name="PartsActivity">
        <activity android:name=".PartsActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <meta-data
                android:name="org.cyanogenmod.settings.summary.receiver"
                android:value="org.cyanogenmod.cmparts.PartsUpdater" />
        </activity>

        <receiver android:name="BootReceiver" android:enabled="true">
        <receiver android:name=".BootReceiver" android:enabled="true">
            <intent-filter android:priority="2147483647">
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <receiver android:name=".RefreshReceiver" android:enabled="true">
        <receiver android:name=".PartsUpdater" android:enabled="true">
            <intent-filter>
                <action android:name="org.cyanogenmod.cmparts.REFRESH_PART" />
                <action android:name="org.cyanogenmod.settings.REFRESH_SUMMARY" />
                <action android:name="cyanogenmod.intent.action.UPDATE_PREFERENCE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>

@@ -98,7 +101,7 @@
                android:resource="@drawable/ic_settings_privacy" />
            <meta-data
                android:name="org.cyanogenmod.settings.summary.receiver"
                android:value="org.cyanogenmod.cmparts.RefreshReceiver" />
                android:value="org.cyanogenmod.cmparts.PartsUpdater" />
            <meta-data
                android:name="org.cyanogenmod.settings.summary.key"
                android:value="privacy_settings" />
@@ -122,7 +125,7 @@
                android:resource="@drawable/ic_settings_buttons" />
            <meta-data
                android:name="org.cyanogenmod.settings.summary.receiver"
                android:value="org.cyanogenmod.cmparts.RefreshReceiver" />
                android:value="org.cyanogenmod.cmparts.PartsUpdater" />
            <meta-data
                android:name="org.cyanogenmod.settings.summary.key"
                android:value="button_settings" />
@@ -146,7 +149,7 @@
                android:resource="@drawable/ic_settings_profiles" />
            <meta-data
                android:name="org.cyanogenmod.settings.summary.receiver"
                android:value="org.cyanogenmod.cmparts.RefreshReceiver" />
                android:value="org.cyanogenmod.cmparts.PartsUpdater" />
            <meta-data
                android:name="org.cyanogenmod.settings.summary.key"
                android:value="profiles_settings" />
@@ -170,7 +173,7 @@
                android:resource="@drawable/ic_settings_statusbar" />
            <meta-data
                android:name="org.cyanogenmod.settings.summary.receiver"
                android:value="org.cyanogenmod.cmparts.RefreshReceiver" />
                android:value="org.cyanogenmod.cmparts.PartsUpdater" />
            <meta-data
                android:name="org.cyanogenmod.settings.summary.key"
                android:value="status_bar_settings" />
+0 −7
Original line number Diff line number Diff line
@@ -173,13 +173,6 @@ public class PartsActivity extends SettingsDrawerActivity implements
        return super.onOptionsItemSelected(item);
    }

    public void notifyPartChanged(PartInfo part) {
        Intent i = new Intent(PartsList.ACTION_PART_CHANGED);
        i.getExtras().putString(PartsList.EXTRA_PART_KEY, part.getName());
        i.getExtras().putParcelable(PartsList.EXTRA_PART, part);
        sendBroadcast(i);
    }

    public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
                                     CharSequence titleText, Fragment resultTo, int resultRequestCode) {
        String title = null;
+106 −0
Original line number Diff line number Diff line
@@ -15,33 +15,21 @@
 */
package org.cyanogenmod.cmparts;

import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;

import org.cyanogenmod.internal.cmparts.PartInfo;
import org.cyanogenmod.internal.cmparts.PartsList;
import org.cyanogenmod.platform.internal.Manifest;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static org.cyanogenmod.internal.cmparts.PartsList.ACTION_PART_CHANGED;
import cyanogenmod.preference.RemotePreferenceUpdater;
import cyanogenmod.preference.SettingsHelper;

import static cyanogenmod.preference.RemotePreference.EXTRA_KEY;
import static cyanogenmod.preference.RemotePreference.EXTRA_SUMMARY;
import static org.cyanogenmod.internal.cmparts.PartsList.EXTRA_PART;
import static org.cyanogenmod.internal.cmparts.PartsList.EXTRA_PART_KEY;
import static org.cyanogenmod.internal.cmparts.PartsList.EXTRA_PART_SUMMARY;

/**
 * PartsRefresher keeps remote UI clients up to date with any changes in the
@@ -57,33 +45,13 @@ import static org.cyanogenmod.internal.cmparts.PartsList.EXTRA_PART_SUMMARY;
 * Parts can also call refreshPart to send an asynchronous update to any
 * active remote components via broadcast.
 */
public class PartsRefresher {
public class PartsUpdater extends RemotePreferenceUpdater {

    private static final String TAG = PartsRefresher.class.getSimpleName();
    private static final String TAG = PartsUpdater.class.getSimpleName();

    public static final String FIELD_NAME_SUMMARY_PROVIDER = "SUMMARY_PROVIDER";

    private static PartsRefresher sInstance;

    private final Context mContext;

    private final Handler mHandler;

    private final SettingsObserver mObserver;

    private PartsRefresher(Context context) {
        super();
        mContext = context;
        mHandler = new Handler();
        mObserver = new SettingsObserver();
    }

    public static synchronized PartsRefresher get(Context context) {
        if (sInstance == null) {
            sInstance = new PartsRefresher(context);
        }
        return sInstance;
    }
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);

    private Refreshable.SummaryProvider getPartSummary(PartInfo pi) {
        final Class<?> clazz;
@@ -107,117 +75,32 @@ public class PartsRefresher {
        return null;
    }

    boolean updateExtras(String key, Bundle bundle) {
        final PartInfo pi = PartsList.get(mContext).getPartInfo(key);
    @Override
    protected boolean fillResultExtras(Context context, String key, Bundle bundle) {
        final PartInfo pi = PartsList.get(context).getPartInfo(key);
        if (pi == null) {
            Log.w(TAG, "Part not found: " + key);
            return false;
        }

        bundle.putString(EXTRA_KEY, key);

        final Refreshable.SummaryProvider si = getPartSummary(pi);
        if (si == null) {
            return false;
        if (si != null) {
            pi.setSummary(si.getSummary(context, key));
            bundle.putString(EXTRA_SUMMARY, pi.getSummary());
        }

        String summary = si.getSummary(mContext, key);

        if (Objects.equals(summary, pi.getSummary())) {
            return false;
        }
        if (DEBUG) Log.d(TAG, "fillResultExtras key=" + key +
                         " part=" + pi.toString());

        pi.setSummary(si.getSummary(mContext, key));
        bundle.putString(EXTRA_PART_KEY, key);
        bundle.putString(EXTRA_PART_SUMMARY, pi.getSummary());
        bundle.putParcelable(EXTRA_PART, pi);
        return true;
    }

    public void refreshPart(String key) {
        final Intent i = new Intent(ACTION_PART_CHANGED);
        final Bundle extras = new Bundle();
        if (updateExtras(key, extras)) {
            i.putExtras(extras);
            mContext.sendBroadcastAsUser(i, UserHandle.CURRENT, Manifest.permission.MANAGE_PARTS);
        }
    }

    public void addTrigger(Refreshable listener, Uri... contentUris) {
        mObserver.register(listener, contentUris);
    }

    public void removeTrigger(Refreshable listener) {
        mObserver.unregister(listener);
    }

    public interface Refreshable {
        public void onRefresh(Context context, Uri what);

    public interface Refreshable extends SettingsHelper.OnSettingsChangeListener {
        public interface SummaryProvider {
            public String getSummary(Context context, String key);
        }
    }

    private final class SettingsObserver extends ContentObserver {

        private final Map<Refreshable, Set<Uri>> mTriggers = new ArrayMap<>();
        private final List<Uri> mRefs = new ArrayList<>();

        private final ContentResolver mResolver;

        public SettingsObserver() {
            super(mHandler);

            mResolver = mContext.getContentResolver();
        }

        public void register(Refreshable listener, Uri... contentUris) {
            synchronized (mRefs) {
                Set<Uri> uris = mTriggers.get(listener);
                if (uris == null) {
                    uris = new ArraySet<Uri>();
                    mTriggers.put(listener, uris);
                }
                for (Uri contentUri : contentUris) {
                    uris.add(contentUri);
                    if (!mRefs.contains(contentUri)) {
                        mResolver.registerContentObserver(contentUri, false, this);
                        listener.onRefresh(mContext, null);
                    }
                    mRefs.add(contentUri);
                }
            }
        }

        public void unregister(Refreshable listener) {
            synchronized (mRefs) {
                Set<Uri> uris = mTriggers.remove(listener);
                if (uris != null) {
                    for (Uri uri : uris) {
                        mRefs.remove(uri);
                    }
                }
                if (mRefs.size() == 0) {
                    mResolver.unregisterContentObserver(this);
                }
            }
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            synchronized (mRefs) {
                super.onChange(selfChange, uri);

                final Set<Refreshable> notify = new ArraySet<>();
                for (Map.Entry<Refreshable, Set<Uri>> entry : mTriggers.entrySet()) {
                    if (entry.getValue().contains(uri)) {
                        notify.add(entry.getKey());
                    }
                }

                for (Refreshable listener : notify) {
                    listener.onRefresh(mContext, uri);
                }
            }
        }
    }

}
+0 −55
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The CyanogenMod 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.
 */
package org.cyanogenmod.cmparts;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import static org.cyanogenmod.internal.cmparts.PartsList.ACTION_REFRESH_PART;
import static org.cyanogenmod.internal.cmparts.PartsList.EXTRA_PART_KEY;

public class RefreshReceiver extends BroadcastReceiver {

    /* for Settings dashboard tiles */
    private static final String ACTION_REFRESH_SUMMARY =
            "org.cyanogenmod.settings.REFRESH_SUMMARY";

    /**
     * Receiver which handles clients requesting a summary update. A client may send
     * the REFERSH_PART or REFRESH_SUMMARY actions via sendOrderedBroadcast,
     * and we will reply immediately.
     *
     * @param context
     * @param intent
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("REFRESH-PARTS", intent.toString());
        if (isOrderedBroadcast() && (ACTION_REFRESH_PART.equals(intent.getAction()) ||
                ACTION_REFRESH_SUMMARY.equals(intent.getAction()))) {
            final String key = intent.getStringExtra(EXTRA_PART_KEY);
            if (key != null &&
                    PartsRefresher.get(context).updateExtras(key, getResultExtras(true))) {
                setResultCode(Activity.RESULT_OK);
                return;
            }
        }
        abortBroadcast();
    }
}
+20 −11
Original line number Diff line number Diff line
@@ -53,11 +53,13 @@ import org.cyanogenmod.cmparts.widget.LayoutPreference;
import java.util.Arrays;
import java.util.UUID;

import cyanogenmod.preference.SettingsHelper;

/**
 * Base class for Settings fragments, with some helper functions and dialog management.
 */
public abstract class SettingsPreferenceFragment extends PreferenceFragment
        implements DialogCreatable, PartsRefresher.Refreshable {
        implements DialogCreatable, PartsUpdater.Refreshable {

    /**
     * The Help Uri Resource key. This can be passed as an extra argument when creating the
@@ -188,8 +190,8 @@ public abstract class SettingsPreferenceFragment extends PreferenceFragment
    }

    @Override
    public void onRefresh(Context context, Uri contentUri) {
        PartsRefresher.get(context).refreshPart(getPreferenceScreen().getKey());
    public void onSettingsChanged(Uri contentUri) {
        PartsUpdater.notifyChanged(getActivity(), getPreferenceScreen().getKey());
    }

    public void showLoadingWhenEmpty() {
@@ -495,9 +497,11 @@ public abstract class SettingsPreferenceFragment extends PreferenceFragment
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        PartsRefresher.get(activity).addTrigger(this,
        synchronized (mTriggerUris) {
            SettingsHelper.get(activity).startWatching(this,
                    mTriggerUris.toArray(new Uri[mTriggerUris.size()]));
        }
    }

    @Override
    public void onDetach() {
@@ -507,17 +511,22 @@ public abstract class SettingsPreferenceFragment extends PreferenceFragment
                mDialogFragment = null;
            }
        }
        PartsRefresher.get(getActivity()).removeTrigger(this);
        synchronized (mTriggerUris) {
            SettingsHelper.get(getActivity()).stopWatching(this);
            mTriggerUris.clear();
        }
        super.onDetach();
    }

    protected void addTrigger(Uri... contentUris) {
    protected void watch(Uri... contentUris) {
        synchronized (mTriggerUris) {
            mTriggerUris.addAll(Arrays.asList(contentUris));
            if (!isDetached()) {
            PartsRefresher.get(getActivity()).addTrigger(this,
                SettingsHelper.get(getActivity()).startWatching(this,
                        mTriggerUris.toArray(new Uri[mTriggerUris.size()]));
            }
        }
    }

    // Dialog management

Loading