Add QR code scanner button to search tab

This commit is contained in:
Grishka 2024-02-28 23:48:00 +03:00
parent e9dde114b7
commit 58259d9fb0
4 changed files with 151 additions and 73 deletions

View File

@ -282,77 +282,7 @@ public class ProfileQrCodeFragment extends AppKitFragment{
if(scannerIntent.resolveActivity(getActivity().getPackageManager())!=null){ if(scannerIntent.resolveActivity(getActivity().getPackageManager())!=null){
startActivityForResult(scannerIntent, SCAN_RESULT); startActivityForResult(scannerIntent, SCAN_RESULT);
}else{ }else{
ProgressDialog progress=new ProgressDialog(getActivity()); BarcodeScanner.installScannerModule(themeWrapper, ()->startActivityForResult(scannerIntent, SCAN_RESULT));
progress.setMessage(getString(R.string.loading));
progress.setCancelable(false);
progress.show();
GmsClient.getModuleInstallerService(getActivity(), new GmsClient.ServiceConnectionCallback<>(){
@Override
public void onSuccess(IModuleInstallService service, int connectionID){
ApiFeatureRequest req=new ApiFeatureRequest();
req.callingPackage=getActivity().getPackageName();
Feature feature=new Feature();
feature.name="mlkit.barcode.ui";
feature.version=1;
feature.oldVersion=-1;
req.features=List.of(feature);
req.urgent=true;
try{
service.installModules(new IModuleInstallCallbacks.Stub(){
@Override
public void onModuleAvailabilityResponse(Status status, ModuleAvailabilityResponse response) throws RemoteException{}
@Override
public void onModuleInstallResponse(Status status, ModuleInstallResponse response) throws RemoteException{}
@Override
public void onModuleInstallIntentResponse(Status status, ModuleInstallIntentResponse response) throws RemoteException{}
@Override
public void onStatus(Status status) throws RemoteException{}
}, req, new IModuleInstallStatusListener.Stub(){
@Override
public void onModuleInstallStatusUpdate(ModuleInstallStatusUpdate statusUpdate) throws RemoteException{
if(statusUpdate.installState==ModuleInstallStatusUpdate.STATE_COMPLETED){
Runnable r=new Runnable(){
@Override
public void run(){
if(scannerIntent.resolveActivity(getActivity().getPackageManager())!=null){
progress.dismiss();
startActivityForResult(scannerIntent, SCAN_RESULT);
}else{
codeContainer.postDelayed(this, 100);
}
}
};
getActivity().runOnUiThread(r);
GmsClient.disconnectFromService(getActivity(), connectionID);
}else if(statusUpdate.installState==ModuleInstallStatusUpdate.STATE_FAILED || statusUpdate.installState==ModuleInstallStatusUpdate.STATE_CANCELED){
getActivity().runOnUiThread(()->{
progress.dismiss();
Toast.makeText(themeWrapper, R.string.error, Toast.LENGTH_SHORT).show();
});
GmsClient.disconnectFromService(getActivity(), connectionID);
}
}
});
}catch(RemoteException e){
Log.e(TAG, "onSuccess: ", e);
getActivity().runOnUiThread(()->{
progress.dismiss();
Toast.makeText(themeWrapper, R.string.error, Toast.LENGTH_SHORT).show();
});
GmsClient.disconnectFromService(getActivity(), connectionID);
}
}
@Override
public void onError(Exception error){
Log.e(TAG, "onError() called with: error = ["+error+"]");
Toast.makeText(themeWrapper, R.string.error, Toast.LENGTH_SHORT).show();
progress.dismiss();
}
});
} }
return true; return true;
} }

View File

@ -1,6 +1,9 @@
package org.joinmastodon.android.fragments.discover; package org.joinmastodon.android.fragments.discover;
import android.app.Activity;
import android.app.Fragment; import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
@ -11,9 +14,14 @@ import android.widget.FrameLayout;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import org.joinmastodon.android.MainActivity;
import org.joinmastodon.android.R; import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.ScrollableToTop; import org.joinmastodon.android.fragments.ScrollableToTop;
import org.joinmastodon.android.googleservices.GmsClient;
import org.joinmastodon.android.googleservices.barcodescanner.Barcode;
import org.joinmastodon.android.googleservices.barcodescanner.BarcodeScanner;
import org.joinmastodon.android.model.SearchResult; import org.joinmastodon.android.model.SearchResult;
import org.joinmastodon.android.ui.OutlineProviders; import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.SimpleViewHolder; import org.joinmastodon.android.ui.SimpleViewHolder;
@ -33,6 +41,7 @@ import me.grishka.appkit.utils.V;
public class DiscoverFragment extends AppKitFragment implements ScrollableToTop, OnBackPressedListener{ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop, OnBackPressedListener{
private static final int QUERY_RESULT=937; private static final int QUERY_RESULT=937;
private static final int SCAN_RESULT=456;
private TabLayout tabLayout; private TabLayout tabLayout;
private ViewPager2 pager; private ViewPager2 pager;
@ -40,7 +49,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
private TabLayoutMediator tabLayoutMediator; private TabLayoutMediator tabLayoutMediator;
private boolean searchActive; private boolean searchActive;
private FrameLayout searchView; private FrameLayout searchView;
private ImageButton searchBack; private ImageButton searchBack, searchScanQR;
private TextView searchText; private TextView searchText;
private View tabsDivider; private View tabsDivider;
@ -52,6 +61,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
private String accountID; private String accountID;
private String currentQuery; private String currentQuery;
private Intent scannerIntent;
@Override @Override
public void onCreate(Bundle savedInstanceState){ public void onCreate(Bundle savedInstanceState){
@ -60,6 +70,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
setRetainInstance(true); setRetainInstance(true);
accountID=getArguments().getString("account"); accountID=getArguments().getString("account");
scannerIntent=BarcodeScanner.createIntent(Barcode.FORMAT_QR_CODE, false, true);
} }
@Nullable @Nullable
@ -174,6 +185,12 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
tabLayout.setVisibility(View.GONE); tabLayout.setVisibility(View.GONE);
searchView.setVisibility(View.VISIBLE); searchView.setVisibility(View.VISIBLE);
} }
searchScanQR=view.findViewById(R.id.search_scan_qr);
if(!GmsClient.isGooglePlayServicesAvailable(getActivity())){
searchScanQR.setVisibility(View.GONE);
}else{
searchScanQR.setOnClickListener(v->openQrScanner());
}
View searchWrap=view.findViewById(R.id.search_wrap); View searchWrap=view.findViewById(R.id.search_wrap);
searchWrap.setOutlineProvider(OutlineProviders.roundedRect(28)); searchWrap.setOutlineProvider(OutlineProviders.roundedRect(28));
@ -268,6 +285,28 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
} }
} }
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode==SCAN_RESULT && resultCode==Activity.RESULT_OK && BarcodeScanner.isValidResult(data)){
Barcode code=BarcodeScanner.getResult(data);
if(code!=null){
if(code.rawValue.startsWith("https:") || code.rawValue.startsWith("http:")){
((MainActivity)getActivity()).handleURL(Uri.parse(code.rawValue), accountID);
}else{
Toast.makeText(getActivity(), R.string.link_not_supported, Toast.LENGTH_SHORT).show();
}
}
}
}
private void openQrScanner(){
if(scannerIntent.resolveActivity(getActivity().getPackageManager())!=null){
startActivityForResult(scannerIntent, SCAN_RESULT);
}else{
BarcodeScanner.installScannerModule(getActivity(), ()->startActivityForResult(scannerIntent, SCAN_RESULT));
}
}
private class DiscoverPagerAdapter extends RecyclerView.Adapter<SimpleViewHolder>{ private class DiscoverPagerAdapter extends RecyclerView.Adapter<SimpleViewHolder>{
@NonNull @NonNull
@Override @Override

View File

@ -1,12 +1,35 @@
package org.joinmastodon.android.googleservices.barcodescanner; package org.joinmastodon.android.googleservices.barcodescanner;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.os.Parcel; import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.Feature;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.common.moduleinstall.ModuleAvailabilityResponse;
import com.google.android.gms.common.moduleinstall.ModuleInstallIntentResponse;
import com.google.android.gms.common.moduleinstall.ModuleInstallResponse;
import com.google.android.gms.common.moduleinstall.ModuleInstallStatusUpdate;
import com.google.android.gms.common.moduleinstall.internal.ApiFeatureRequest;
import com.google.android.gms.common.moduleinstall.internal.IModuleInstallCallbacks;
import com.google.android.gms.common.moduleinstall.internal.IModuleInstallService;
import com.google.android.gms.common.moduleinstall.internal.IModuleInstallStatusListener;
import org.joinmastodon.android.MastodonApp; import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.googleservices.GmsClient;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.List;
public class BarcodeScanner{ public class BarcodeScanner{
private static final String TAG="BarcodeScanner";
public static Intent createIntent(int formats, boolean allowManualInout, boolean enableAutoZoom){ public static Intent createIntent(int formats, boolean allowManualInout, boolean enableAutoZoom){
Intent intent=new Intent().setPackage("com.google.android.gms").setAction("com.google.android.gms.mlkit.ACTION_SCAN_BARCODE"); Intent intent=new Intent().setPackage("com.google.android.gms").setAction("com.google.android.gms.mlkit.ACTION_SCAN_BARCODE");
String appName; String appName;
@ -35,4 +58,79 @@ public class BarcodeScanner{
parcel.recycle(); parcel.recycle();
return barcode; return barcode;
} }
public static void installScannerModule(Context context, Runnable onSuccess){
ProgressDialog progress=new ProgressDialog(context);
progress.setMessage(context.getString(R.string.loading));
progress.setCancelable(false);
progress.show();
GmsClient.getModuleInstallerService(context, new GmsClient.ServiceConnectionCallback<>(){
@Override
public void onSuccess(IModuleInstallService service, int connectionID){
ApiFeatureRequest req=new ApiFeatureRequest();
req.callingPackage=context.getPackageName();
Feature feature=new Feature();
feature.name="mlkit.barcode.ui";
feature.version=1;
feature.oldVersion=-1;
req.features=List.of(feature);
req.urgent=true;
try{
service.installModules(new IModuleInstallCallbacks.Stub(){
@Override
public void onModuleAvailabilityResponse(Status status, ModuleAvailabilityResponse response) throws RemoteException{}
@Override
public void onModuleInstallResponse(Status status, ModuleInstallResponse response) throws RemoteException{}
@Override
public void onModuleInstallIntentResponse(Status status, ModuleInstallIntentResponse response) throws RemoteException{}
@Override
public void onStatus(Status status) throws RemoteException{}
}, req, new IModuleInstallStatusListener.Stub(){
@Override
public void onModuleInstallStatusUpdate(ModuleInstallStatusUpdate statusUpdate) throws RemoteException{
if(statusUpdate.installState==ModuleInstallStatusUpdate.STATE_COMPLETED){
Intent scannerIntent=createIntent(0, false, false);
Runnable r=new Runnable(){
@Override
public void run(){
if(scannerIntent.resolveActivity(context.getPackageManager())!=null){
progress.dismiss();
onSuccess.run();
}else{
UiUtils.runOnUiThread(this, 100);
}
}
};
UiUtils.runOnUiThread(r);
GmsClient.disconnectFromService(context, connectionID);
}else if(statusUpdate.installState==ModuleInstallStatusUpdate.STATE_FAILED || statusUpdate.installState==ModuleInstallStatusUpdate.STATE_CANCELED){
UiUtils.runOnUiThread(()->{
progress.dismiss();
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show();
});
GmsClient.disconnectFromService(context, connectionID);
}
}
});
}catch(RemoteException e){
Log.e(TAG, "onSuccess: ", e);
UiUtils.runOnUiThread(()->{
progress.dismiss();
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show();
});
GmsClient.disconnectFromService(context, connectionID);
}
}
@Override
public void onError(Exception error){
Log.e(TAG, "onError() called with: error = ["+error+"]");
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show();
progress.dismiss();
}
});
}
} }

View File

@ -30,8 +30,9 @@
<TextView <TextView
android:id="@+id/search_text" android:id="@+id/search_text"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|start" android:gravity="center_vertical|start"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:singleLine="true" android:singleLine="true"
@ -39,6 +40,16 @@
android:textAppearance="@style/m3_body_large" android:textAppearance="@style/m3_body_large"
android:text="@string/search_mastodon"/> android:text="@string/search_mastodon"/>
<ImageButton
android:id="@+id/search_scan_qr"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="8dp"
android:contentDescription="@string/scan_qr_code"
android:background="@drawable/bg_round_ripple"
android:tint="?colorM3OnSurfaceVariant"
android:src="@drawable/ic_qr_code_scanner_24px"/>
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>