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.samples; 18 19 import android.content.Intent; 20 import android.os.Bundle; 21 import android.util.Log; 22 import android.view.Menu; 23 import android.view.MenuItem; 24 import android.view.View; 25 import android.widget.ArrayAdapter; 26 import android.widget.ListView; 27 import android.widget.TextView; 28 import android.widget.Toast; 29 30 import androidx.annotation.WorkerThread; 31 import androidx.appcompat.app.AppCompatActivity; 32 import androidx.appsearch.app.AppSearchEnvironmentFactory; 33 import androidx.appsearch.debugview.samples.model.Note; 34 import androidx.appsearch.debugview.view.AppSearchDebugActivity; 35 import androidx.core.content.ContextCompat; 36 37 import com.google.common.util.concurrent.FutureCallback; 38 import com.google.common.util.concurrent.Futures; 39 import com.google.common.util.concurrent.ListenableFuture; 40 import com.google.common.util.concurrent.ListeningExecutorService; 41 import com.google.common.util.concurrent.MoreExecutors; 42 import com.google.common.util.concurrent.SettableFuture; 43 import com.google.gson.Gson; 44 import com.google.gson.JsonArray; 45 import com.google.gson.JsonObject; 46 47 import org.jspecify.annotations.NonNull; 48 import org.jspecify.annotations.Nullable; 49 50 import java.io.IOException; 51 import java.io.InputStreamReader; 52 import java.util.ArrayList; 53 import java.util.List; 54 55 /** 56 * Default Activity for AppSearch Debug View Sample App 57 * 58 * <p>This activity reads sample data, converts it into {@link Note} objects, and then indexes 59 * them into AppSearch. 60 * 61 * <p>Each sample note's text is added to the list view for display. 62 */ 63 public class NotesActivity extends AppCompatActivity { 64 private static final String DB_NAME = "notesDb"; 65 private static final String SAMPLE_NOTES_FILENAME = "sample_notes.json"; 66 private static final String TAG = "NotesActivity"; 67 68 private final SettableFuture<NotesAppSearchManager> mNotesAppSearchManagerFuture = 69 SettableFuture.create(); 70 private ArrayAdapter<Note> mNotesAdapter; 71 private ListView mListView; 72 private TextView mLoadingView; 73 private ListeningExecutorService mBackgroundExecutor; 74 private List<Note> mSampleNotes; 75 76 @Override onCreate(@ullable Bundle savedInstanceState)77 protected void onCreate(@Nullable Bundle savedInstanceState) { 78 super.onCreate(savedInstanceState); 79 setContentView(R.layout.activity_notes); 80 81 mListView = findViewById(R.id.list_view); 82 mLoadingView = findViewById(R.id.text_view); 83 84 mBackgroundExecutor = MoreExecutors.listeningDecorator(AppSearchEnvironmentFactory 85 .getEnvironmentInstance().createCachedThreadPoolExecutor()); 86 87 mNotesAppSearchManagerFuture.setFuture(NotesAppSearchManager.createNotesAppSearchManager( 88 getApplicationContext(), mBackgroundExecutor)); 89 ListenableFuture<List<Note>> sampleNotesFuture = 90 mBackgroundExecutor.submit(() -> loadSampleNotes()); 91 92 ListenableFuture<Void> insertNotesFuture = 93 Futures.whenAllSucceed(mNotesAppSearchManagerFuture, sampleNotesFuture).call( 94 () -> { 95 mSampleNotes = Futures.getDone(sampleNotesFuture); 96 Futures.getDone(mNotesAppSearchManagerFuture).insertNotes( 97 mSampleNotes).get(); 98 return null; 99 }, mBackgroundExecutor); 100 101 Futures.addCallback(insertNotesFuture, 102 new FutureCallback<Void>() { 103 @Override 104 public void onSuccess(Void result) { 105 displayNotes(); 106 } 107 108 @Override 109 public void onFailure(@NonNull Throwable t) { 110 Toast.makeText(NotesActivity.this, "Failed to insert notes " 111 + "into AppSearch.", Toast.LENGTH_LONG).show(); 112 Log.e(TAG, "Failed to insert notes into AppSearch.", t); 113 } 114 }, ContextCompat.getMainExecutor(this)); 115 } 116 117 @Override onCreateOptionsMenu(@onNull Menu menu)118 public boolean onCreateOptionsMenu(@NonNull Menu menu) { 119 getMenuInflater().inflate(R.menu.debug_menu, menu); 120 121 return true; 122 } 123 124 @Override onOptionsItemSelected(@onNull MenuItem item)125 public boolean onOptionsItemSelected(@NonNull MenuItem item) { 126 if (item.getItemId() == R.id.app_search_debug) { 127 Intent intent = new Intent(this, AppSearchDebugActivity.class); 128 intent.putExtra(AppSearchDebugActivity.DB_INTENT_KEY, DB_NAME); 129 intent.putExtra(AppSearchDebugActivity.STORAGE_TYPE_INTENT_KEY, 130 AppSearchDebugActivity.STORAGE_TYPE_LOCAL); 131 startActivity(intent); 132 return true; 133 } 134 135 return false; 136 } 137 138 @Override onStop()139 protected void onStop() { 140 Futures.whenAllSucceed(mNotesAppSearchManagerFuture).call(() -> { 141 Futures.getDone(mNotesAppSearchManagerFuture).close(); 142 return null; 143 }, mBackgroundExecutor); 144 145 super.onStop(); 146 } 147 148 @WorkerThread loadSampleNotes()149 private List<Note> loadSampleNotes() { 150 List<Note> sampleNotes = new ArrayList<>(); 151 Gson gson = new Gson(); 152 try (InputStreamReader r = new InputStreamReader( 153 getAssets().open(SAMPLE_NOTES_FILENAME))) { 154 JsonObject samplesJson = gson.fromJson(r, JsonObject.class); 155 JsonArray sampleJsonArr = samplesJson.getAsJsonArray("data"); 156 for (int i = 0; i < sampleJsonArr.size(); ++i) { 157 JsonObject noteJson = sampleJsonArr.get(i).getAsJsonObject(); 158 sampleNotes.add(new Note.Builder().setId(noteJson.get("id").getAsString()) 159 .setNamespace(noteJson.get("namespace").getAsString()) 160 .setText(noteJson.get("noteText").getAsString()) 161 .build() 162 ); 163 } 164 } catch (IOException e) { 165 Toast.makeText(NotesActivity.this, "Failed to load sample notes ", 166 Toast.LENGTH_LONG).show(); 167 Log.e(TAG, "Sample notes IO failed: ", e); 168 } 169 return sampleNotes; 170 } 171 displayNotes()172 private void displayNotes() { 173 mNotesAdapter = new ArrayAdapter<>(this, 174 android.R.layout.simple_list_item_1, mSampleNotes); 175 mListView.setAdapter(mNotesAdapter); 176 177 mLoadingView.setVisibility(View.GONE); 178 mListView.setVisibility(View.VISIBLE); 179 } 180 } 181