1 /* 2 * Copyright (C) 2019 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.bips.ui; 18 19 import android.app.ActionBar; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.ServiceConnection; 24 import android.os.Bundle; 25 import android.os.IBinder; 26 import android.print.PrintJobInfo; 27 import android.print.PrinterId; 28 import android.printservice.PrintService; 29 import android.util.Log; 30 import android.view.MenuItem; 31 import android.view.View; 32 import android.widget.LinearLayout; 33 import android.widget.Toast; 34 35 import androidx.fragment.app.FragmentActivity; 36 import androidx.fragment.app.FragmentManager; 37 import androidx.lifecycle.ViewModelProvider; 38 39 import com.android.bips.BuiltInPrintService; 40 import com.android.bips.R; 41 import com.android.bips.discovery.ConnectionListener; 42 import com.android.bips.discovery.DiscoveredPrinter; 43 import com.android.bips.discovery.Discovery; 44 import com.android.bips.flags.Flags; 45 import com.android.bips.p2p.P2pPrinterConnection; 46 import com.android.bips.p2p.P2pUtils; 47 48 import java.net.InetAddress; 49 import java.net.UnknownHostException; 50 import java.util.concurrent.ExecutorService; 51 import java.util.concurrent.Executors; 52 53 /** 54 * Launched by system in response to a "More Options" request while tracking a printer. 55 */ 56 public class MoreOptionsActivity extends FragmentActivity implements ServiceConnection, 57 Discovery.Listener { 58 private static final String TAG = MoreOptionsActivity.class.getSimpleName(); 59 60 private static final boolean DEBUG = false; 61 62 private BuiltInPrintService mPrintService; 63 PrinterId mPrinterId; 64 DiscoveredPrinter mPrinter; 65 InetAddress mPrinterAddress; 66 public static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID"; 67 private static final String TAG_RECOMMENDATION_FRAGMENT = "recommendation_fragment"; 68 private static final String TAG_PRINTER_INFORMATION_FRAGMENT = "printer_information_fragment"; 69 private PrinterInformationViewModel mPrinterInformationViewModel; 70 private LinearLayout mLlRecommendedServices; 71 private LinearLayout mLlRecommendedServicesSummary; 72 private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); 73 private P2pPrinterConnection mP2pPrinterConnection; 74 75 @Override onCreate(Bundle savedInstanceState)76 protected void onCreate(Bundle savedInstanceState) { 77 super.onCreate(savedInstanceState); 78 if (getIntent().hasExtra(PrintService.EXTRA_PRINT_JOB_INFO)) { 79 PrintJobInfo jobInfo = 80 getIntent().getParcelableExtra(PrintService.EXTRA_PRINT_JOB_INFO); 81 mPrinterId = jobInfo.getPrinterId(); 82 } else if (getIntent().hasExtra(EXTRA_PRINTER_ID)) { 83 mPrinterId = getIntent().getParcelableExtra(EXTRA_PRINTER_ID); 84 } else { 85 if (DEBUG) Log.i(TAG, "No job info or printer info to show. Exiting."); 86 finish(); 87 return; 88 } 89 ActionBar actionBar = getActionBar(); 90 if (actionBar != null) { 91 actionBar.setDisplayHomeAsUpEnabled(true); 92 } 93 if ((Flags.printerInfoDetails())) { 94 setContentView(R.layout.combined_info_recs); 95 mPrinterInformationViewModel = 96 new ViewModelProvider(this).get(PrinterInformationViewModel.class); 97 getSupportFragmentManager().popBackStack(null, 98 FragmentManager.POP_BACK_STACK_INCLUSIVE); 99 mLlRecommendedServicesSummary = findViewById(R.id.ll_recommended_services_summary); 100 mLlRecommendedServices = findViewById(R.id.ll_recommended_services); 101 mLlRecommendedServices.setOnClickListener(view -> { 102 if (getSupportFragmentManager().findFragmentByTag(TAG_RECOMMENDATION_FRAGMENT) 103 == null) { 104 MoreOptionsFragment fragment = new MoreOptionsFragment(); 105 getSupportFragmentManager().beginTransaction() 106 .replace(R.id.fragment_container, fragment, TAG_RECOMMENDATION_FRAGMENT) 107 .setReorderingAllowed(true) 108 .addToBackStack(null) 109 .commit(); 110 mLlRecommendedServices.setVisibility(View.GONE); 111 mLlRecommendedServicesSummary.setVisibility(View.GONE); 112 } 113 }); 114 getSupportFragmentManager().addOnBackStackChangedListener( 115 () -> { 116 if (getSupportFragmentManager().getBackStackEntryCount() == 0) { 117 mLlRecommendedServices.setVisibility(View.VISIBLE); 118 mLlRecommendedServicesSummary.setVisibility(View.VISIBLE); 119 if (mPrinter != null) { 120 setTitle(mPrinter.name); 121 } 122 } 123 }); 124 setTitle(R.string.information); 125 } else { 126 getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 127 } 128 129 ViewUtil.setWindowInsetsListener(getWindow().getDecorView(), this); 130 } 131 132 @Override onOptionsItemSelected(MenuItem item)133 public boolean onOptionsItemSelected(MenuItem item) { 134 switch (item.getItemId()) { 135 case android.R.id.home: 136 finish(); 137 return true; 138 } 139 return super.onOptionsItemSelected(item); 140 } 141 142 @Override onStart()143 protected void onStart() { 144 super.onStart(); 145 bindService(new Intent(this, BuiltInPrintService.class), this, 146 Context.BIND_AUTO_CREATE); 147 } 148 149 @Override onStop()150 protected void onStop() { 151 super.onStop(); 152 153 if (mP2pPrinterConnection != null) { 154 mP2pPrinterConnection.close(); 155 mP2pPrinterConnection = null; 156 } 157 158 if (mPrintService != null) { 159 if ((Flags.printerInfoDetails())) { 160 mPrinterInformationViewModel.stopPrinterStatusMonitor(mPrintService); 161 } 162 mPrintService.getDiscovery().stop(this); 163 } 164 unbindService(this); 165 } 166 167 @Override onDestroy()168 protected void onDestroy() { 169 super.onDestroy(); 170 mExecutorService.shutdownNow(); 171 } 172 173 @Override onServiceConnected(ComponentName name, IBinder service)174 public void onServiceConnected(ComponentName name, IBinder service) { 175 mPrintService = BuiltInPrintService.getInstance(); 176 mPrintService.getDiscovery().start(this); 177 } 178 179 @Override onServiceDisconnected(ComponentName name)180 public void onServiceDisconnected(ComponentName name) { 181 mPrintService = null; 182 } 183 184 @Override onPrinterFound(DiscoveredPrinter printer)185 public void onPrinterFound(DiscoveredPrinter printer) { 186 // Return when P2P connection is in progress 187 if (mP2pPrinterConnection != null) { 188 return; 189 } 190 191 if (printer.getUri().toString().equals(mPrinterId.getLocalId())) { 192 // We discovered a printer matching the job's PrinterId, so show recommendations 193 if (P2pUtils.isP2p(printer)) { 194 // Printer is not connected on p2p interface 195 connectP2P(printer); 196 } else { 197 loadPrinterInfoFragment(printer); 198 } 199 } 200 } 201 connectP2P(DiscoveredPrinter printer)202 private void connectP2P(DiscoveredPrinter printer) { 203 Toast.makeText(mPrintService, getString(R.string.connecting_to, printer.name), 204 Toast.LENGTH_LONG).show(); 205 206 mP2pPrinterConnection = new P2pPrinterConnection(mPrintService, printer, 207 new ConnectionListener() { 208 @Override 209 public void onConnectionComplete(DiscoveredPrinter printer) { 210 if (DEBUG) Log.d(TAG, "onConnectionComplete(), printer = " + printer); 211 if (printer != null && printer.paths.size() > 1) { 212 loadPrinterInfoFragment( 213 new DiscoveredPrinter(printer.uuid, printer.name, 214 printer.paths.get(1), printer.location)); 215 } else { 216 Toast.makeText(mPrintService, R.string.failed_printer_connection, 217 Toast.LENGTH_LONG).show(); 218 if (mP2pPrinterConnection != null) { 219 mP2pPrinterConnection.close(); 220 mP2pPrinterConnection = null; 221 } 222 } 223 } 224 225 @Override 226 public void onConnectionDelayed(boolean delayed) { 227 if (delayed) { 228 Toast.makeText(mPrintService, R.string.connect_hint_text, 229 Toast.LENGTH_LONG).show(); 230 } 231 } 232 }); 233 } 234 loadPrinterInfoFragment(DiscoveredPrinter printer)235 private void loadPrinterInfoFragment(DiscoveredPrinter printer) { 236 mPrinter = printer; 237 setTitle(mPrinter.name); 238 if ((Flags.printerInfoDetails())) { 239 if (printer.path != null) { 240 mPrinterInformationViewModel.getPrinterStatus(printer.path, mPrintService); 241 } else { 242 mPrinterInformationViewModel.setPrinterUnavailableLiveData(true); 243 } 244 } 245 // Network operation in non UI thread 246 mExecutorService.execute(() -> { 247 try { 248 mPrinterAddress = InetAddress.getByName(mPrinter.path.getHost()); 249 // No need for continued discovery after we find the printer. 250 mPrintService.getDiscovery().stop(this); 251 if (!mExecutorService.isShutdown() && mPrintService != null) { 252 mPrintService.getMainHandler().post(() -> { 253 if ((Flags.printerInfoDetails())) { 254 if (getSupportFragmentManager().findFragmentByTag( 255 TAG_PRINTER_INFORMATION_FRAGMENT) == null) { 256 PrinterInformationFragment informationFragment = 257 new PrinterInformationFragment(); 258 getSupportFragmentManager().beginTransaction() 259 .replace(R.id.fragment_container, informationFragment, 260 TAG_PRINTER_INFORMATION_FRAGMENT) 261 .commit(); 262 } 263 mPrintService.getCapabilitiesCache().request(mPrinter, true, 264 capabilities -> { 265 if (capabilities != null) { 266 mPrinterInformationViewModel.setPrinterCapsLiveData( 267 capabilities); 268 } else { 269 mPrinterInformationViewModel.setPrinterUnavailableLiveData( 270 true); 271 Toast.makeText(mPrintService, 272 R.string.failed_printer_connection, 273 Toast.LENGTH_LONG).show(); 274 } 275 }); 276 } else { 277 if (getFragmentManager().findFragmentByTag(TAG_RECOMMENDATION_FRAGMENT) 278 == null) { 279 MoreOptionsFragment fragment = new MoreOptionsFragment(); 280 getSupportFragmentManager().beginTransaction() 281 .replace(android.R.id.content, fragment, 282 TAG_RECOMMENDATION_FRAGMENT) 283 .commit(); 284 } 285 } 286 }); 287 } 288 } catch (UnknownHostException ignored) { 289 } 290 }); 291 } 292 293 @Override onPrinterLost(DiscoveredPrinter printer)294 public void onPrinterLost(DiscoveredPrinter printer) { 295 // Ignore 296 } 297 } 298