1 /* 2 * Copyright 2021 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 androidx.appsearch.debugview.view; 18 19 import android.os.Bundle; 20 import android.util.Log; 21 import android.widget.Toast; 22 23 import androidx.annotation.IntDef; 24 import androidx.annotation.RestrictTo; 25 import androidx.appsearch.app.AppSearchEnvironmentFactory; 26 import androidx.appsearch.debugview.DebugAppSearchManager; 27 import androidx.appsearch.debugview.R; 28 import androidx.appsearch.exceptions.AppSearchException; 29 import androidx.fragment.app.FragmentActivity; 30 31 import com.google.common.util.concurrent.Futures; 32 import com.google.common.util.concurrent.ListenableFuture; 33 import com.google.common.util.concurrent.ListeningExecutorService; 34 import com.google.common.util.concurrent.MoreExecutors; 35 36 import org.jspecify.annotations.NonNull; 37 import org.jspecify.annotations.Nullable; 38 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 42 /** 43 * Debug Activity for AppSearch. 44 * 45 * <p>This activity provides a view of all the documents that have been put into an application's 46 * AppSearch database. The database is specified by creating an {@link android.content.Intent} 47 * with extras specifying the database name and the AppSearch storage type. 48 * 49 * <p>To launch this activity, declare it in the application's manifest: 50 * <pre> 51 * <activity android:name="androidx.appsearch.debugview.view.AppSearchDebugActivity" /> 52 * </pre> 53 * 54 * <p>Next, create an {@link android.content.Intent} from the activity that will launch the debug 55 * activity. Add the database name as an extra with key: {@link #DB_INTENT_KEY} and the storage 56 * type, which can be either {@link #STORAGE_TYPE_LOCAL} or {@link #STORAGE_TYPE_PLATFORM} with 57 * key: {@link #STORAGE_TYPE_INTENT_KEY}. 58 * 59 * <p>Example of launching the debug activity for local storage: 60 * <pre> 61 * Intent intent = new Intent(this, AppSearchDebugActivity.class); 62 * intent.putExtra(AppSearchDebugActivity.DB_INTENT_KEY, DB_NAME); 63 * intent.putExtra(AppSearchDebugActivity.STORAGE_TYPE_INTENT_KEY, 64 * AppSearchDebugActivity.STORAGE_TYPE_LOCAL); 65 * startActivity(intent); 66 * </pre> 67 * 68 * @exportToFramework:hide 69 */ 70 @RestrictTo(RestrictTo.Scope.LIBRARY) 71 public class AppSearchDebugActivity extends FragmentActivity { 72 private static final String TAG = "AppSearchDebugActivity"; 73 public static final String TARGET_PACKAGE_NAME_INTENT_KEY = "targetPackageName"; 74 public static final String DB_INTENT_KEY = "databaseName"; 75 public static final String STORAGE_TYPE_INTENT_KEY = "storageType"; 76 public static final String SEARCH_TYPE_INTENT_KEY = "searchType"; 77 78 private String mDbName; 79 private ListenableFuture<DebugAppSearchManager> mDebugAppSearchManager; 80 private ListeningExecutorService mBackgroundExecutor; 81 82 @IntDef(value = { 83 STORAGE_TYPE_LOCAL, 84 STORAGE_TYPE_PLATFORM, 85 }) 86 @Retention(RetentionPolicy.SOURCE) 87 public @interface StorageType { 88 } 89 90 public static final int STORAGE_TYPE_LOCAL = 0; 91 public static final int STORAGE_TYPE_PLATFORM = 1; 92 93 @IntDef(value = { 94 SEARCH_TYPE_LOCAL, 95 SEARCH_TYPE_GLOBAL, 96 }) 97 @Retention(RetentionPolicy.SOURCE) 98 public @interface SearchType { 99 } 100 101 public static final int SEARCH_TYPE_LOCAL = 0; 102 public static final int SEARCH_TYPE_GLOBAL = 1; 103 104 @Override onCreate(@ullable Bundle savedInstanceState)105 protected void onCreate(@Nullable Bundle savedInstanceState) { 106 super.onCreate(savedInstanceState); 107 setContentView(R.layout.activity_appsearchdebug); 108 109 mBackgroundExecutor = MoreExecutors.listeningDecorator(AppSearchEnvironmentFactory 110 .getEnvironmentInstance().createCachedThreadPoolExecutor()); 111 mDbName = getIntent().getExtras().getString(DB_INTENT_KEY); 112 String targetPackageName = 113 getIntent().getExtras().getString(TARGET_PACKAGE_NAME_INTENT_KEY); 114 @StorageType int storageType = 115 getIntent().getExtras().getInt(STORAGE_TYPE_INTENT_KEY); 116 @SearchType int searchType = 117 getIntent().getExtras().getInt(SEARCH_TYPE_INTENT_KEY); 118 try { 119 mDebugAppSearchManager = DebugAppSearchManager.createAsync( 120 getApplicationContext(), mBackgroundExecutor, mDbName, targetPackageName, 121 storageType, searchType); 122 } catch (AppSearchException e) { 123 Toast.makeText(getApplicationContext(), 124 "Failed to initialize AppSearch: " + e.getMessage(), 125 Toast.LENGTH_LONG).show(); 126 Log.e(TAG, "Failed to initialize AppSearch.", e); 127 } 128 129 MenuFragment menuFragment = new MenuFragment(); 130 getSupportFragmentManager() 131 .beginTransaction() 132 .replace(R.id.fragment_container, menuFragment) 133 .commit(); 134 } 135 136 @Override onStop()137 protected void onStop() { 138 Futures.whenAllSucceed(mDebugAppSearchManager).call(() -> { 139 Futures.getDone(mDebugAppSearchManager).close(); 140 return null; 141 }, mBackgroundExecutor); 142 143 super.onStop(); 144 } 145 146 /** 147 * Gets the {@link DebugAppSearchManager} instance created by the activity. 148 */ getDebugAppSearchManager()149 public @NonNull ListenableFuture<DebugAppSearchManager> getDebugAppSearchManager() { 150 return mDebugAppSearchManager; 151 } 152 153 /** 154 * Gets the {@link ListeningExecutorService} instance created by the activity. 155 */ getBackgroundExecutor()156 public @NonNull ListeningExecutorService getBackgroundExecutor() { 157 return mBackgroundExecutor; 158 } 159 } 160