fr.kwiatkowski.apktrack.ui.AppDisplayFragment.java Source code

Java tutorial

Introduction

Here is the source code for fr.kwiatkowski.apktrack.ui.AppDisplayFragment.java

Source

/*
 * Copyright (c) 2015
 *
 * ApkTrack 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.
 *
 * ApkTrack 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 ApkTrack.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.kwiatkowski.apktrack.ui;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;
import de.greenrobot.event.EventBus;
import de.greenrobot.event.EventBusException;
import fr.kwiatkowski.apktrack.MainActivity;
import fr.kwiatkowski.apktrack.R;
import fr.kwiatkowski.apktrack.model.InstalledApp;
import fr.kwiatkowski.apktrack.model.comparator.AlphabeticalComparator;
import fr.kwiatkowski.apktrack.model.comparator.StatusComparator;
import fr.kwiatkowski.apktrack.service.message.CreateToastMessage;
import fr.kwiatkowski.apktrack.service.message.ModelModifiedMessage;

import java.util.Comparator;
import java.util.List;

/**
 * Fragment containing the list of applications installed on the device, and their update status.
 */
public class AppDisplayFragment extends Fragment {

    public static final String APP_DISPLAY_FRAGMENT_SOURCE = "appdisplayfragment";

    public AppDisplayFragment() {
    }

    private RecyclerView _recycler_view;
    private CoordinatorLayout _coordinator_layout;
    private LinearLayout _spinner;
    private AppAdapter _app_adapter;
    private Comparator<InstalledApp> _comparator;

    // --------------------------------------------------------------------------------------------

    @Override
    public void onResume() {
        super.onResume();

        // Register for sticky events in a separate thread.
        // When registering, the latest stiky is also delivered. It may contain many events to
        // process, which is why this is kept out of the UI thread.
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    EventBus.getDefault().registerSticky(AppDisplayFragment.this, 1);
                } catch (EventBusException ignored) {
                } // The fragment may already be registered.
            }
        }).start();

    }

    @Override
    public void onPause() {
        super.onPause();
        EventBus.getDefault().unregister(this);
    }

    // --------------------------------------------------------------------------------------------

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_main, container, false);
        _coordinator_layout = (CoordinatorLayout) v.findViewById(R.id.snackbar);
        _spinner = (LinearLayout) v.findViewById(R.id.spinner);
        _recycler_view = (RecyclerView) v.findViewById(R.id.recycler_view);
        _recycler_view.setLayoutManager(new LinearLayoutManager(getActivity()));
        _recycler_view
                .addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
        _app_adapter = new AppAdapter(getContext());
        _recycler_view.setAdapter(_app_adapter);
        return v;
    }

    // --------------------------------------------------------------------------------------------

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // Set the comparator depending on the settings
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(view.getContext());
        String sort_type = pref.getString(SettingsFragment.KEY_PREF_SORT_TYPE, SettingsFragment.ALPHA_SORT);
        if (sort_type.equals(SettingsFragment.ALPHA_SORT)) {
            _comparator = new AlphabeticalComparator();
        } else {
            _comparator = new StatusComparator();
        }

        // Reset the currently checking state for all apps in case ApkTrack was terminated forcefully during
        // update checks. savedInstanceState will not be null if an orientation change caused the Application
        // instance to be destroyed and recreated.
        if (savedInstanceState == null) {
            InstalledApp.executeQuery("UPDATE installed_app SET _iscurrentlychecking = 0");
        }

        // Do not freeze the UI while retrieving the application list
        new Thread(new Runnable() {
            @Override
            public void run() {
                final List<InstalledApp> installed_apps = _initialize_data();
                _run_on_ui_thread(new Runnable() {
                    @Override
                    public void run() {
                        _app_adapter.add_apps(installed_apps);
                        // Handle the swipe movement.
                        ItemTouchHelper ith = new ItemTouchHelper(
                                new SwipeHandler(_coordinator_layout, _app_adapter));
                        ith.attachToRecyclerView(_recycler_view);
                        // Handle clicks
                        _spinner.setVisibility(View.GONE);
                    }
                });
            }
        }).start();
    }

    // --------------------------------------------------------------------------------------------

    /**
     * This method is called when a MessageModifiedEvent is posted on the event bus.
     * @param m The message to process.
     */
    public void onEvent(ModelModifiedMessage m) {
        if (_app_adapter == null) {
            return;
        }

        List<Pair<ModelModifiedMessage.event_type, String>> events;
        try {
            events = m.get_events();
        } catch (ModelModifiedMessage.EventAlreadyProcessedException ignored) {
            return; // Event was already handled.
        }

        for (final Pair<ModelModifiedMessage.event_type, String> event : events) {
            // An application was removed
            if (event.first == ModelModifiedMessage.event_type.APP_REMOVED) {
                _app_adapter.remove_app(event.second);
            }

            // An application was added
            else if (event.first == ModelModifiedMessage.event_type.APP_ADDED) {
                InstalledApp target = InstalledApp.find_app(event.second);
                if (target == null) {
                    return;
                }
                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
                // Only add the app if it is not a system app, or if system apps are currently displayed.
                if (!target.is_system()
                        || (prefs != null && prefs.getBoolean(SettingsFragment.KEY_PREF_SHOW_SYSTEM, false))) {
                    _app_adapter.add_app(target);
                }
            }

            // An application was updated.
            else if (event.first == ModelModifiedMessage.event_type.APP_UPDATED) {
                _recycler_view.post(new Runnable() {
                    @Override
                    public void run() {
                        _app_adapter.notify_app_updated(event.second);
                    }
                });
            }
        }
    }

    // --------------------------------------------------------------------------------------------

    /**
     * This method is called when a Service needs the Activity to display a Toast, if it is
     * running.
     * @param m The message containing the text to display.
     */
    public void onEventMainThread(CreateToastMessage m) {
        // TODO: Set a timer to prevent toast flood?
        Toast.makeText(getContext(), m.get_message(), Toast.LENGTH_SHORT).show();
    }

    // --------------------------------------------------------------------------------------------

    /**
     * Remove apps from an AppDisplayFragment. The call is forwarded to the AppAdapter.
     * @param to_remove A list of apps to remove from the list.
     * @see AppAdapter#remove_apps(List)
     */
    public void remove_apps(List<InstalledApp> to_remove) {
        _app_adapter.remove_apps(to_remove);
    }

    // --------------------------------------------------------------------------------------------

    /**
     * Filters apps in an AppDisplayFragment. The call is forwarded to the AppAdapter.
     * @param to_keep A list of apps to keep in the list. All the others will be removed.
     * @see AppAdapter#filter_apps(List)
     */
    public void filter_apps(List<InstalledApp> to_keep) {
        _app_adapter.filter_apps(to_keep);
    }

    // --------------------------------------------------------------------------------------------

    /**
     * Add apps to an AppDisplayFragment. The call is forwarded to the AppAdapter.
     * @param to_add A list of apps to add from the list.
     * @see AppAdapter#add_apps(List)
     */
    public void add_apps(List<InstalledApp> to_add) {
        _app_adapter.add_apps(to_add);
    }

    // --------------------------------------------------------------------------------------------

    /**
     *  Restores the app list to its original state.
     *  Any apps filtered out by searches are reinserted, but ignored apps stay ignored.
     */
    public void restore_apps() {
        List<InstalledApp> to_reinsert = _initialize_data(); // Get the full app list.
        _app_adapter.add_apps(to_reinsert, false);
    }

    // --------------------------------------------------------------------------------------------

    /**
     * Causes the adapter to sort the app list based on the comparator selected in the user
     * preferences.
     */
    public void sort() {
        _app_adapter.sort();
    }

    // --------------------------------------------------------------------------------------------

    /**
     * This method loads the list of installed applications from the database,
     * or generates it if no data exists.
     */
    private List<InstalledApp> _initialize_data() {
        String where_clause = "_isignored = 0";
        // Check whether system apps should be displayed
        Activity activity = getActivity();
        if (activity != null) {
            SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(activity);
            boolean show_system = pref.getBoolean(SettingsFragment.KEY_PREF_SHOW_SYSTEM, false);
            if (!show_system) {
                where_clause += " and _systemapp = 0";
            }
        }

        List<InstalledApp> installed_apps = InstalledApp.find(InstalledApp.class, where_clause);
        if (installed_apps.size() == 0 && activity != null) // Database is empty
        {
            Log.v(MainActivity.TAG, "Populating database...");
            InstalledApp.generate_applist_from_system(activity.getPackageManager());
            installed_apps = InstalledApp.find(InstalledApp.class, "_systemapp = 0 AND _isignored = 0");
            Log.v(MainActivity.TAG,
                    "...database populated. " + InstalledApp.count(InstalledApp.class) + " records created.");
            // Enable the "dhow system apps" button now that there may be system apps.
            activity.invalidateOptionsMenu();
        } else if (activity != null) {
            Log.v(MainActivity.TAG, installed_apps.size() + " records read from the database.");
        }
        return installed_apps;
    }

    // --------------------------------------------------------------------------------------------

    /**
     * Helper function which runs a given Runnable inside the UI Thread.
     * @param r The Runnable to run.
     */
    private void _run_on_ui_thread(Runnable r) {
        Activity activity = getActivity();
        if (activity != null) {
            activity.runOnUiThread(r);
        }
    }
}