/* * Firetweet - Twitter client for Android * * Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.getlantern.firetweet.loader; import android.content.AsyncTaskLoader; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import org.getlantern.firetweet.Constants; import org.getlantern.firetweet.util.ParseUtils; import org.getlantern.firetweet.util.PermissionsManager; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ExtensionsListLoader extends AsyncTaskLoader<List<ExtensionsListLoader.ExtensionInfo>> implements Constants { private PackageIntentReceiver mPackageObserver; private final InterestingConfigChanges mLastConfig = new InterestingConfigChanges(); private final PackageManager mPackageManager; public ExtensionsListLoader(final Context context, final PackageManager pm) { super(context); mPackageManager = pm; } @Override public List<ExtensionInfo> loadInBackground() { final List<ApplicationInfo> apps = mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA); final List<ExtensionInfo> extensions = new ArrayList<ExtensionInfo>(); for (final ApplicationInfo info : apps) { final Bundle meta = info.metaData; if (meta != null && meta.getBoolean(METADATA_KEY_EXTENSION, false)) { extensions.add(new ExtensionInfo(info, mPackageManager)); } } return extensions; } /** * Handles a request to completely reset the Loader. */ @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); // Stop monitoring for changes. if (mPackageObserver != null) { getContext().unregisterReceiver(mPackageObserver); mPackageObserver = null; } } /** * Handles a request to start the Loader. */ @Override protected void onStartLoading() { // Start watching for changes in the app data. if (mPackageObserver == null) { mPackageObserver = new PackageIntentReceiver(this); } // Has something interesting in the configuration changed since we // last built the app list? final boolean configChange = mLastConfig.applyNewConfig(getContext().getResources()); if (takeContentChanged() || configChange) { // If the data has changed since the last time it was loaded // or is not currently available, start a load. forceLoad(); } } /** * Handles a request to stop the Loader. */ @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } public static class ExtensionInfo implements Comparable<ExtensionInfo> { public final String[] permissions; public final String label, description; public final String pname, settings; public final Drawable icon; ExtensionInfo(final ApplicationInfo info, final PackageManager pm) { final Bundle meta = info.metaData; final String permissionString = meta.getString(METADATA_KEY_EXTENSION_PERMISSIONS); permissions = permissionString != null ? permissionString .split(PermissionsManager.SEPARATOR_PERMISSION_REGEX) : null; settings = meta.getString(METADATA_KEY_EXTENSION_SETTINGS); icon = info.loadIcon(pm); pname = info.packageName; label = ParseUtils.parseString(info.loadLabel(pm), pname); description = ParseUtils.parseString(info.loadDescription(pm)); } @Override public int compareTo(@NonNull final ExtensionInfo another) { return label.compareToIgnoreCase(another.label); } @Override public String toString() { return "ExtensionInfo{" + "permissions=" + Arrays.toString(permissions) + ", label='" + label + '\'' + ", description='" + description + '\'' + ", pname='" + pname + '\'' + ", settings='" + settings + '\'' + ", icon=" + icon + '}'; } } /** * Helper for determining if the configuration has changed in an interesting * way so we need to rebuild the app list. */ public static class InterestingConfigChanges { final Configuration mLastConfiguration = new Configuration(); int mLastDensity; boolean applyNewConfig(final Resources res) { final int configChanges = mLastConfiguration.updateFrom(res.getConfiguration()); final boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi; if (densityChanged || (configChanges & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) { mLastDensity = res.getDisplayMetrics().densityDpi; return true; } return false; } } /** * Helper class to look for interesting changes to the installed apps so * that the loader can be updated. */ public static class PackageIntentReceiver extends BroadcastReceiver { final ExtensionsListLoader mLoader; public PackageIntentReceiver(final ExtensionsListLoader loader) { mLoader = loader; final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); mLoader.getContext().registerReceiver(this, filter); // Register for events related to sdcard installation. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { final IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mLoader.getContext().registerReceiver(this, sdFilter); } } @Override public void onReceive(final Context context, final Intent intent) { // Tell the loader about the change. mLoader.onContentChanged(); } } }