Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3;
     18 
     19 import android.accounts.Account;
     20 import android.accounts.AccountManager;
     21 import android.animation.ObjectAnimator;
     22 import android.animation.PropertyValuesHolder;
     23 import android.app.ActivityManager;
     24 import android.content.Context;
     25 import android.content.SharedPreferences;
     26 import android.graphics.drawable.Drawable;
     27 import android.os.Bundle;
     28 import android.os.UserManager;
     29 import android.provider.Settings;
     30 import android.view.LayoutInflater;
     31 import android.view.View;
     32 import android.view.View.OnClickListener;
     33 import android.view.View.OnLongClickListener;
     34 import android.view.ViewGroup;
     35 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
     36 import android.view.accessibility.AccessibilityManager;
     37 
     38 class LauncherClings implements OnClickListener {
     39     private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
     40     private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
     41 
     42     private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides";
     43 
     44     private static final boolean DISABLE_CLINGS = false;
     45 
     46     private static final int SHOW_CLING_DURATION = 250;
     47     private static final int DISMISS_CLING_DURATION = 200;
     48 
     49     // New Secure Setting in L
     50     private static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
     51 
     52     private Launcher mLauncher;
     53     private LayoutInflater mInflater;
     54 
     55     /** Ctor */
     56     public LauncherClings(Launcher launcher) {
     57         mLauncher = launcher;
     58         mInflater = LayoutInflater.from(mLauncher);
     59     }
     60 
     61     @Override
     62     public void onClick(View v) {
     63         int id = v.getId();
     64         if (id == R.id.cling_dismiss_migration_use_default) {
     65             // Disable the migration cling
     66             dismissMigrationCling();
     67         } else if (id == R.id.cling_dismiss_migration_copy_apps) {
     68             // Copy the shortcuts from the old database
     69             LauncherModel model = mLauncher.getModel();
     70             model.resetLoadedState(false, true);
     71             model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
     72                     LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
     73                             | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
     74             // Set the flag to skip the folder cling
     75             String spKey = LauncherAppState.getSharedPreferencesKey();
     76             SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
     77             SharedPreferences.Editor editor = sp.edit();
     78             editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
     79             editor.apply();
     80             // Disable the migration cling
     81             dismissMigrationCling();
     82         } else if (id == R.id.cling_dismiss_longpress_info) {
     83             dismissLongPressCling();
     84         }
     85     }
     86 
     87     /**
     88      * Shows the migration cling.
     89      *
     90      * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
     91      * package was not preinstalled and there exists a db to migrate from.
     92      */
     93     public void showMigrationCling() {
     94         mLauncher.hideWorkspaceSearchAndHotseat();
     95 
     96         ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
     97         View inflated = mInflater.inflate(R.layout.migration_cling, root);
     98         inflated.findViewById(R.id.cling_dismiss_migration_copy_apps).setOnClickListener(this);
     99         inflated.findViewById(R.id.cling_dismiss_migration_use_default).setOnClickListener(this);
    100     }
    101 
    102     private void dismissMigrationCling() {
    103         mLauncher.showWorkspaceSearchAndHotseat();
    104         Runnable dismissCb = new Runnable() {
    105             public void run() {
    106                 Runnable cb = new Runnable() {
    107                     public void run() {
    108                         // Show the longpress cling next
    109                         showLongPressCling(false);
    110                     }
    111                 };
    112                 dismissCling(mLauncher.findViewById(R.id.migration_cling), cb,
    113                         MIGRATION_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
    114             }
    115         };
    116         mLauncher.getWorkspace().post(dismissCb);
    117     }
    118 
    119     public void showLongPressCling(boolean showWelcome) {
    120         ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
    121         View cling = mInflater.inflate(R.layout.longpress_cling, root, false);
    122 
    123         cling.setOnLongClickListener(new OnLongClickListener() {
    124 
    125             @Override
    126             public boolean onLongClick(View v) {
    127                 mLauncher.getWorkspace().enterOverviewMode();
    128                 dismissLongPressCling();
    129                 return true;
    130             }
    131         });
    132 
    133         final ViewGroup content = (ViewGroup) cling.findViewById(R.id.cling_content);
    134         mInflater.inflate(showWelcome ? R.layout.longpress_cling_welcome_content
    135                 : R.layout.longpress_cling_content, content);
    136         content.findViewById(R.id.cling_dismiss_longpress_info).setOnClickListener(this);
    137 
    138         if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
    139             Drawable bg = new BorderCropDrawable(mLauncher.getResources().getDrawable(R.drawable.cling_bg),
    140                     true, true, true, false);
    141             content.setBackground(bg);
    142         }
    143 
    144         root.addView(cling);
    145 
    146         if (showWelcome) {
    147             // This is the first cling being shown. No need to animate.
    148             return;
    149         }
    150 
    151         // Animate
    152         content.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    153 
    154             @Override
    155             public void onGlobalLayout() {
    156                 content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    157 
    158                 ObjectAnimator anim;
    159                 if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
    160                     content.setTranslationY(-content.getMeasuredHeight());
    161                     anim = LauncherAnimUtils.ofFloat(content, "translationY", 0);
    162                 } else {
    163                     content.setScaleX(0);
    164                     content.setScaleY(0);
    165                     PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1);
    166                     PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1);
    167                     anim = LauncherAnimUtils.ofPropertyValuesHolder(content, scaleX, scaleY);
    168                 }
    169 
    170                 anim.setDuration(SHOW_CLING_DURATION);
    171                 anim.setInterpolator(new LogDecelerateInterpolator(100, 0));
    172                 anim.start();
    173             }
    174         });
    175     }
    176 
    177     private void dismissLongPressCling() {
    178         Runnable dismissCb = new Runnable() {
    179             public void run() {
    180                 dismissCling(mLauncher.findViewById(R.id.longpress_cling), null,
    181                         WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
    182             }
    183         };
    184         mLauncher.getWorkspace().post(dismissCb);
    185     }
    186 
    187     /** Hides the specified Cling */
    188     private void dismissCling(final View cling, final Runnable postAnimationCb,
    189                               final String flag, int duration) {
    190         // To catch cases where siblings of top-level views are made invisible, just check whether
    191         // the cling is directly set to GONE before dismissing it.
    192         if (cling != null && cling.getVisibility() != View.GONE) {
    193             final Runnable cleanUpClingCb = new Runnable() {
    194                 public void run() {
    195                     cling.setVisibility(View.GONE);
    196                     mLauncher.getSharedPrefs().edit()
    197                         .putBoolean(flag, true)
    198                         .apply();
    199                     if (postAnimationCb != null) {
    200                         postAnimationCb.run();
    201                     }
    202                 }
    203             };
    204             if (duration <= 0) {
    205                 cleanUpClingCb.run();
    206             } else {
    207                 cling.animate().alpha(0).setDuration(duration).withEndAction(cleanUpClingCb);
    208             }
    209         }
    210     }
    211 
    212     /** Returns whether the clings are enabled or should be shown */
    213     private boolean areClingsEnabled() {
    214         if (DISABLE_CLINGS) {
    215             return false;
    216         }
    217 
    218         // disable clings when running in a test harness
    219         if(ActivityManager.isRunningInTestHarness()) return false;
    220 
    221         // Disable clings for accessibility when explore by touch is enabled
    222         final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
    223                 Launcher.ACCESSIBILITY_SERVICE);
    224         if (a11yManager.isTouchExplorationEnabled()) {
    225             return false;
    226         }
    227 
    228         // Restricted secondary users (child mode) will potentially have very few apps
    229         // seeded when they start up for the first time. Clings won't work well with that
    230         boolean supportsLimitedUsers =
    231                 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
    232         Account[] accounts = AccountManager.get(mLauncher).getAccounts();
    233         if (supportsLimitedUsers && accounts.length == 0) {
    234             UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
    235             Bundle restrictions = um.getUserRestrictions();
    236             if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
    237                 return false;
    238             }
    239         }
    240         if (Settings.Secure.getInt(mLauncher.getContentResolver(), SKIP_FIRST_USE_HINTS, 0)
    241                 == 1) {
    242             return false;
    243         }
    244         return true;
    245     }
    246 
    247     public boolean shouldShowFirstRunOrMigrationClings() {
    248         SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
    249         return areClingsEnabled() &&
    250             !sharedPrefs.getBoolean(WORKSPACE_CLING_DISMISSED_KEY, false) &&
    251             !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
    252     }
    253 
    254     public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) {
    255         SharedPreferences prefs = ctx.getSharedPreferences(
    256                 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
    257         SharedPreferences.Editor editor = prefs.edit();
    258         editor.putBoolean(WORKSPACE_CLING_DISMISSED_KEY, true);
    259         editor.commit();
    260     }
    261 }
    262