| /** |
| * Copyright (c) 2018 The Android Open Source 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.android.launcher3; |
| |
| import static com.android.launcher3.PinnedAppListViewModel.PINNED_APPS_KEY; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.app.AlertDialog; |
| import android.app.Application; |
| import android.content.Intent; |
| import android.content.SharedPreferences; |
| import android.content.res.Configuration; |
| import android.os.Bundle; |
| import android.view.MenuInflater; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.ViewAnimationUtils; |
| import android.view.inputmethod.InputMethodManager; |
| import android.widget.GridView; |
| import android.widget.ImageButton; |
| import android.widget.PopupMenu; |
| |
| import androidx.fragment.app.FragmentActivity; |
| import androidx.fragment.app.FragmentManager; |
| import androidx.lifecycle.ViewModelProvider; |
| import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory; |
| |
| import com.google.android.material.circularreveal.cardview.CircularRevealCardView; |
| import com.google.android.material.floatingactionbutton.FloatingActionButton; |
| |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * Secondary launcher activity. It's launch mode is configured as "singleTop" to allow showing on |
| * multiple displays and to ensure a single instance per each display. |
| */ |
| public class SecondaryDisplayLauncher extends FragmentActivity implements AppPickedCallback, |
| PopupMenu.OnMenuItemClickListener { |
| |
| private AppListAdapter mAppListAdapter; |
| private AppListAdapter mPinnedAppListAdapter; |
| private CircularRevealCardView mAppDrawerView; |
| private FloatingActionButton mFab; |
| |
| private boolean mAppDrawerShown; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.secondary_display_launcher); |
| |
| mAppDrawerView = findViewById(R.id.FloatingSheet); |
| mFab = findViewById(R.id.FloatingActionButton); |
| |
| mFab.setOnClickListener((View v) -> { |
| showAppDrawer(true); |
| }); |
| |
| final ViewModelProvider viewModelProvider = new ViewModelProvider(getViewModelStore(), |
| new AndroidViewModelFactory((Application) getApplicationContext())); |
| |
| mPinnedAppListAdapter = new AppListAdapter(this); |
| final GridView pinnedAppGridView = findViewById(R.id.pinned_app_grid); |
| pinnedAppGridView.setAdapter(mPinnedAppListAdapter); |
| pinnedAppGridView.setOnItemClickListener((adapterView, view, position, id) -> { |
| final AppEntry entry = mPinnedAppListAdapter.getItem(position); |
| launch(entry.getLaunchIntent()); |
| }); |
| final PinnedAppListViewModel pinnedAppListViewModel = |
| viewModelProvider.get(PinnedAppListViewModel.class); |
| pinnedAppListViewModel.getPinnedAppList().observe(this, data -> { |
| mPinnedAppListAdapter.setData(data); |
| }); |
| |
| mAppListAdapter = new AppListAdapter(this); |
| final GridView appGridView = findViewById(R.id.app_grid); |
| appGridView.setAdapter(mAppListAdapter); |
| appGridView.setOnItemClickListener((adapterView, view, position, id) -> { |
| final AppEntry entry = mAppListAdapter.getItem(position); |
| launch(entry.getLaunchIntent()); |
| }); |
| final AppListViewModel appListViewModel = viewModelProvider.get(AppListViewModel.class); |
| appListViewModel.getAppList().observe(this, data -> { |
| mAppListAdapter.setData(data); |
| }); |
| |
| ImageButton optionsButton = findViewById(R.id.OptionsButton); |
| optionsButton.setOnClickListener((View v) -> { |
| PopupMenu popup = new PopupMenu(this,v); |
| popup.setOnMenuItemClickListener(this); |
| MenuInflater inflater = popup.getMenuInflater(); |
| inflater.inflate(R.menu.context_menu, popup.getMenu()); |
| popup.show(); |
| }); |
| } |
| |
| @Override |
| public boolean onMenuItemClick(MenuItem item) { |
| // Respond to picking one of the popup menu items. |
| final int id = item.getItemId(); |
| if (id == R.id.add_app_shortcut) { |
| FragmentManager fm = getSupportFragmentManager(); |
| PinnedAppPickerDialog pickerDialogFragment = |
| PinnedAppPickerDialog.newInstance(mAppListAdapter, this); |
| pickerDialogFragment.show(fm, "fragment_app_picker"); |
| return true; |
| } else if (id == R.id.set_wallpaper) { |
| Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); |
| startActivity(Intent.createChooser(intent, getString(R.string.set_wallpaper))); |
| return true; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public void onConfigurationChanged(Configuration newConfig) { |
| super.onConfigurationChanged(newConfig); |
| showAppDrawer(false); |
| } |
| |
| public void onBackPressed() { |
| // If the app drawer was shown - hide it. Otherwise, not doing anything since we don't want |
| // to close the launcher. |
| showAppDrawer(false); |
| } |
| |
| public void onNewIntent(Intent intent) { |
| super.onNewIntent(intent); |
| |
| if (Intent.ACTION_MAIN.equals(intent.getAction())) { |
| // Hide keyboard. |
| final View v = getWindow().peekDecorView(); |
| if (v != null && v.getWindowToken() != null) { |
| getSystemService(InputMethodManager.class).hideSoftInputFromWindow( |
| v.getWindowToken(), 0); |
| } |
| } |
| |
| // A new intent will bring the launcher to top. Hide the app drawer to reset the state. |
| showAppDrawer(false); |
| } |
| |
| void launch(Intent launchIntent) { |
| launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| try { |
| startActivity(launchIntent); |
| } catch (Exception e) { |
| final AlertDialog.Builder builder = |
| new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert); |
| builder.setTitle(R.string.couldnt_launch) |
| .setMessage(e.getLocalizedMessage()) |
| .setIcon(android.R.drawable.ic_dialog_alert) |
| .show(); |
| } |
| } |
| |
| /** |
| * Store the picked app to persistent pinned list and update the loader. |
| */ |
| @Override |
| public void onAppPicked(AppEntry appEntry) { |
| final SharedPreferences sp = getSharedPreferences(PINNED_APPS_KEY, 0); |
| Set<String> pinnedApps = sp.getStringSet(PINNED_APPS_KEY, null); |
| if (pinnedApps == null) { |
| pinnedApps = new HashSet<String>(); |
| } else { |
| // Always need to create a new object to make sure that the changes are persisted. |
| pinnedApps = new HashSet<String>(pinnedApps); |
| } |
| pinnedApps.add(appEntry.getComponentName().flattenToString()); |
| |
| final SharedPreferences.Editor editor = sp.edit(); |
| editor.putStringSet(PINNED_APPS_KEY, pinnedApps); |
| editor.apply(); |
| } |
| |
| /** |
| * Show/hide app drawer card with animation. |
| */ |
| private void showAppDrawer(boolean show) { |
| if (show == mAppDrawerShown) { |
| return; |
| } |
| |
| final Animator animator = revealAnimator(mAppDrawerView, show); |
| if (show) { |
| mAppDrawerShown = true; |
| mAppDrawerView.setVisibility(View.VISIBLE); |
| mFab.setVisibility(View.INVISIBLE); |
| } else { |
| mAppDrawerShown = false; |
| animator.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| super.onAnimationEnd(animation); |
| mAppDrawerView.setVisibility(View.INVISIBLE); |
| mFab.setVisibility(View.VISIBLE); |
| } |
| }); |
| } |
| animator.start(); |
| } |
| |
| /** |
| * Create reveal/hide animator for app list card. |
| */ |
| private Animator revealAnimator(View view, boolean open) { |
| final int radius = (int) Math.hypot((double) view.getWidth(), (double) view.getHeight()); |
| return ViewAnimationUtils.createCircularReveal(view, view.getRight(), view.getBottom(), |
| open ? 0 : radius, open ? radius : 0); |
| } |
| } |