blob: 40f0b0ffba8bf3a4685a07bb6dd80fddc2650ee5 [file] [log] [blame]
/*
* Copyright (C) 2013 Fairphone 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 com.fairphone.fplauncher3.applifecycle;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.fairphone.fplauncher3.AppInfo;
import com.fairphone.fplauncher3.BubbleTextView;
import com.fairphone.fplauncher3.CellLayout;
import com.fairphone.fplauncher3.DeleteDropTarget;
import com.fairphone.fplauncher3.DeviceProfile;
import com.fairphone.fplauncher3.DragSource;
import com.fairphone.fplauncher3.DropTarget.DragObject;
import com.fairphone.fplauncher3.Folder;
import com.fairphone.fplauncher3.ItemInfo;
import com.fairphone.fplauncher3.Launcher;
import com.fairphone.fplauncher3.LauncherAppState;
import com.fairphone.fplauncher3.LauncherTransitionable;
import com.fairphone.fplauncher3.R;
import com.fairphone.fplauncher3.Workspace;
import com.fairphone.fplauncher3.edgeswipe.editor.AppDiscoverer;
import com.fairphone.fplauncher3.widgets.appswitcher.ApplicationRunInformation;
import java.util.ArrayList;
/**
* Edit favorites activity implements functionality to edit your favorite apps
* that will appear with the edge swipe.
*/
public class AppDrawerView extends FrameLayout implements DragSource, LauncherTransitionable, OnLongClickListener {
private static final String TAG = AppDrawerView.class.getSimpleName();
public static final int DRAGGING_DELAY_MILLIS = 150;
public static final String APP_LIFECYCLE_PREFERENCES = "APP_LIFECYCLE_PREFERENCES";
public static final String APP_AGE_LIMIT_IN_DAYS = "APP_AGE_LIMIT_IN_DAYS";
public static final String CURENT_APP_AGE_LIMIT_IN_DAYS = "APP_AGE_LIMIT_IN_DAYS";
public static final String APP_AGE_LIMIT_CHOISE_RESOURCE = "APP_AGE_LIMIT_CHOISE_RESOURCE";
private AgingAppsListAdapter mAllAppsListAdapter;
private AgingAppsListAdapter mUnusedAppsListAdapter;
private ArrayList<AppInfo> mUsedApps;
private ArrayList<AppInfo> mUnusedApps;
private ExpandedGridview mAllAppsGridView;
private ExpandedGridview mUnusedAppsGridView;
private TextView activeAppsDescription;
private TextView unusedAppsDescription;
private ScrollView mScroll;
private Context mContext;
private Launcher mLauncher;
private AppSearchResultsHelper mSearchHelper;
private boolean mInTransition;
private ImageView app_drawer_settings;
private PopupMenu mAppSettingsPopup;
public AppDrawerView(Context context) {
super(context);
init(context);
}
public AppDrawerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public AppDrawerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@SuppressLint("NewApi")
public AppDrawerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
init(context);
}
protected void init(Context context) {
mContext = context.getApplicationContext();
this.removeAllViews();
View view = inflate(mContext, R.layout.fp_aging_app_drawer, null);
refreshAppLists();
setupAllAppsList(view);
setupSearchButton(view);
addView(view);
((LinearLayout)view).getLayoutTransition().setDuration(300);
}
private void setupSearchButton(View view) {
View searchBtn = view.findViewById(R.id.all_apps_search_btn);
searchBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mSearchHelper.showSearchView();
}
});
}
private void refreshAppLists() {
Pair<ArrayList<AppInfo>, ArrayList<AppInfo>> appLists = AppDiscoverer.getInstance().getUsedAndUnusedApps(mContext);
mUsedApps = appLists.first;
mUnusedApps = appLists.second;
}
public void refreshView(Context context, Launcher launcher) {
mLauncher = launcher;
init(context);
mSearchHelper = new AppSearchResultsHelper(AppDrawerView.this, mLauncher);
}
/**
* Setup the list with all the apps installed on the device.
*
* @param view
*/
@SuppressLint("StringFormatInvalid")
private void setupAllAppsList(View view) {
mScroll = (ScrollView) view.findViewById(R.id.agingDrawerScroll);
mScroll.setSmoothScrollingEnabled(true);
mAllAppsGridView = (ExpandedGridview) view.findViewById(R.id.usedAppsGridView);
mUnusedAppsGridView = (ExpandedGridview) view.findViewById(R.id.unusedAppsGridView);
activeAppsDescription = (TextView) view.findViewById(R.id.activeAppsDescription);
unusedAppsDescription = (TextView) view.findViewById(R.id.unusedAppsDescription);
setupListAdapters();
String noIdleAppMessage = String.format(getResources().getString(R.string.no_idle_apps), "one month");
unusedAppsDescription.setText(noIdleAppMessage);
app_drawer_settings = (ImageView) view.findViewById(R.id.aging_drawer_menu_btn);
setupAppSettingsMenu();
app_drawer_settings.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (v.getAlpha() == 0f) {
//fire the easter egg
Toast.makeText(mContext, R.string.lifecyle_time_chooser_easter_egg, Toast.LENGTH_LONG).show();
v.setAlpha(1f);
} else {
mAppSettingsPopup.show();
}
}
});
view.findViewById(R.id.agingDrawerScroll).setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (mSearchHelper.isActive()) {
mSearchHelper.hideKeyboard();
}
}
return false;
}
});
mAllAppsGridView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mSearchHelper.isActive()) {
mSearchHelper.hideKeyboard();
}
}
return true;
}
});
mUnusedAppsGridView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mSearchHelper.isActive()) {
mSearchHelper.hideKeyboard();
}
}
return true;
}
});
}
private void setupListAdapters() {
setupListAdapter(mAllAppsGridView, mAllAppsListAdapter, mUsedApps, false);
setupListAdapter(mUnusedAppsGridView, mUnusedAppsListAdapter, mUnusedApps, true);
if (mUsedApps.isEmpty()) {
activeAppsDescription.setVisibility(View.VISIBLE);
} else {
activeAppsDescription.setVisibility(View.GONE);
}
if (mUnusedApps.isEmpty()) {
unusedAppsDescription.setVisibility(View.VISIBLE);
} else {
unusedAppsDescription.setVisibility(View.GONE);
}
}
@SuppressLint("StringFormatInvalid")
private void setupAppSettingsMenu() {
mAppSettingsPopup = new PopupMenu(mContext, app_drawer_settings);
mAppSettingsPopup.getMenuInflater()
.inflate(R.menu.aging_drawer_menu, mAppSettingsPopup.getMenu());
MenuItem item = mAppSettingsPopup.getMenu().findItem(getAppIdleChoice(mContext));
if (item == null) {
setAppIdleChoice(mContext, R.id.one_month_to_idle);
item = mAppSettingsPopup.getMenu().findItem(getAppIdleChoice(mContext));
}
item.setChecked(true);
mAppSettingsPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
Resources resources = mContext.getResources();
int itemId = item.getItemId();
String noIdleAppMessage;
switch (itemId) {
case R.id.one_week_to_idle:
ApplicationRunInformation.setAppIdleLimitInDays(mContext, resources.getInteger(R.integer.app_frequent_use_one_week));
noIdleAppMessage = String.format(resources.getString(R.string.no_idle_apps), "one week");
unusedAppsDescription.setText(noIdleAppMessage);
break;
case R.id.two_weeks_to_idle:
ApplicationRunInformation.setAppIdleLimitInDays(mContext, resources.getInteger(R.integer.app_frequent_use_two_weeks));
noIdleAppMessage = String.format(resources.getString(R.string.no_idle_apps), "two weeks");
unusedAppsDescription.setText(noIdleAppMessage);
break;
case R.id.one_month_to_idle:
ApplicationRunInformation.setAppIdleLimitInDays(mContext, resources.getInteger(R.integer.app_frequent_use_one_month));
noIdleAppMessage = String.format(resources.getString(R.string.no_idle_apps), "one month");
unusedAppsDescription.setText(noIdleAppMessage);
default:
ApplicationRunInformation.setAppIdleLimitInDays(mContext, resources.getInteger(R.integer.app_frequent_use_default));
noIdleAppMessage = String.format(resources.getString(R.string.no_idle_apps), "one month");
unusedAppsDescription.setText(noIdleAppMessage);
break;
}
setAppIdleChoice(mContext, itemId);
if (item.isChecked()) {
item.setChecked(false);
} else {
item.setChecked(true);
}
//Redraw the drawer
refreshListAdapters();
return true;
}
});
}
public static int getAppIdleChoice(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences(APP_LIFECYCLE_PREFERENCES, Activity.MODE_PRIVATE);
return sharedPreferences.getInt(APP_AGE_LIMIT_CHOISE_RESOURCE, R.id.two_weeks_to_idle);
}
public static void setAppIdleChoice(Context context, int choiceId) {
SharedPreferences sharedPreferences = context.getSharedPreferences(APP_LIFECYCLE_PREFERENCES, Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt(APP_AGE_LIMIT_CHOISE_RESOURCE, choiceId);
editor.commit();
}
public void setupListAdapter(GridView listView, AgingAppsListAdapter appsListAdapter, ArrayList<AppInfo> appList, boolean isUnused) {
appsListAdapter = new AgingAppsListAdapter(mContext, mLauncher, this, isUnused);
appsListAdapter.setAllApps(appList);
listView.setAdapter(appsListAdapter);
}
public void refreshListAdapters() {
refreshAppLists();
mAllAppsGridView.removeAllViewsInLayout();
mUnusedAppsGridView.removeAllViewsInLayout();
setupListAdapters();
}
private void beginDraggingApplication(View v) {
mLauncher.getWorkspace().beginDragShared(v, this);
}
/**
* Clean up after dragging.
*
* @param target where the item was dragged to (can be null if the item was
* flung)
*/
private void endDragging(View target, boolean isFlingToDelete, boolean success) {
if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
// Exit spring loaded mode if we have not successfully dropped or
// have not handled the
// drop in Workspace
mLauncher.exitSpringLoadedDragMode();
mLauncher.unlockScreenOrientation(false);
} else {
mLauncher.unlockScreenOrientation(false);
}
}
protected boolean beginDragging(final View v) {
if (v instanceof BubbleTextView) {
beginDraggingApplication(v);
}
// We delay entering spring-loaded mode slightly to make sure the UI
// thready is free of any work.
postDelayed(new Runnable() {
@Override
public void run() {
// We don't enter spring-loaded mode if the drag has been
// cancelled
if (mLauncher.getDragController().isDragging()) {
// Go into spring loaded mode (must happen before we
// startDrag())
mLauncher.enterSpringLoadedDragMode();
}
}
}, DRAGGING_DELAY_MILLIS);
return true;
}
@Override
public View getContent() {
if (getChildCount() > 0) {
return getChildAt(0);
}
return null;
}
@Override
public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
mInTransition = true;
}
@Override
public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
}
@Override
public void onLauncherTransitionStep(Launcher l, float t) {
}
@Override
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
mInTransition = false;
// mForceDrawAllChildrenNextFrame = !toWorkspace;
}
@Override
public boolean onLongClick(View v) {
// Return early if this is not initiated from a touch
// if (!v.isInTouchMode()) return false;
// When we have exited all apps or are in transition, disregard long
// clicks
// if (!mLauncher.isAgingAppDrawerVisible() ||
// !mLauncher.isAllAppsVisible() ||
// mLauncher.getWorkspace().isSwitchingState()) return false;
// // Return if global dragging is not enabled
// if (!mLauncher.isDraggingEnabled()) return false;
mLauncher.exitOverviewMode();
mLauncher.hideAgingAppDrawer();
return beginDragging(v);
}
@Override
public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, boolean success) {
// Return early and wait for onFlingToDeleteCompleted if this was the
// result of a fling
if (isFlingToDelete) {
return;
}
endDragging(target, false, success);
// Display an error message if the drag failed due to there not being
// enough space on the
// target layout we were dropping on.
if (!success) {
boolean showOutOfSpaceMessage = false;
if (target instanceof Workspace) {
int currentScreen = mLauncher.getCurrentWorkspaceScreen();
Workspace workspace = (Workspace) target;
CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
ItemInfo itemInfo = (ItemInfo) d.dragInfo;
if (layout != null) {
CellLayout.calculateSpans(itemInfo);
showOutOfSpaceMessage = !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
}
}
if (showOutOfSpaceMessage) {
mLauncher.showOutOfSpaceMessage();
}
d.deferDragViewCleanupPostAnimation = false;
}
}
@Override
public void onFlingToDeleteCompleted() {
// We just dismiss the drag when we fling, so cleanup here
endDragging(null, true, true);
}
@Override
public boolean supportsFlingToDelete() {
return true;
}
@Override
public boolean supportsAppInfoDropTarget() {
return true;
}
@Override
public boolean supportsDeleteDropTarget() {
return false;
}
@Override
public float getIntrinsicIconScaleFactor() {
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
return (float) grid.allAppsIconSizePx / grid.iconSizePx;
}
public void hideSearchOrClose() {
Log.v(TAG, this.getClass().getName() + ".hideSearchOrClose() -> active=" + mSearchHelper.isActive());
if (mSearchHelper.isActive()) {
mSearchHelper.smartHide();
} else {
mLauncher.hideAgingAppDrawer();
}
}
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
hideSearchOrClose();
return true;
} else {
return super.dispatchKeyEventPreIme(event);
}
}
}