Code:
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_HOME);
Intent chooser = Intent.createChooser(launchIntent, "Complete Action using..");
activity.startActivity(chooser);
I don't see any way to tell which Intent (HOME category launcher) was selected. There is no Intent.addOnActionSetListener, and no chooser.addOnIntentChosenListener etc.
So how can I tell which was selected? Do I have to write my own chooser for this?
On Android 5.1+, you can use the three-parameter edition of the createChooser() method, where the last parameter is an IntentSender that you can use to find out what was chosen.
Prior to Android 5.1, there is nothing in Android to let you know what the user chose.
The answer provided by BinHe works but the problem is that a big number of apps is shown. In this solution I use the Intent.ACTION_PICK_ACTIVITY but only the apps compatible with Intent.ACTION_SEND will be shown, and you will know which option the user selected.
public void doSocialShare(String title, String text, String url){
// First search for compatible apps with sharing (Intent.ACTION_SEND)
List<Intent> targetedShareIntents = new ArrayList<Intent>();
Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
shareIntent.setType("text/plain");
// Set title and text to share when the user selects an option.
shareIntent.putExtra(Intent.EXTRA_TITLE, title);
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
shareIntent.putExtra(Intent.EXTRA_TEXT, text);
List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(shareIntent, 0);
if (!resInfo.isEmpty()) {
for (ResolveInfo info : resInfo) {
Intent targetedShare = new Intent(android.content.Intent.ACTION_SEND);
targetedShare.setType("text/plain"); // put here your mime type
targetedShare.setPackage(info.activityInfo.packageName.toLowerCase());
targetedShareIntents.add(targetedShare);
}
// Then show the ACTION_PICK_ACTIVITY to let the user select it
Intent intentPick = new Intent();
intentPick.setAction(Intent.ACTION_PICK_ACTIVITY);
// Set the title of the dialog
intentPick.putExtra(Intent.EXTRA_TITLE, title);
intentPick.putExtra(Intent.EXTRA_INTENT, shareIntent);
intentPick.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray());
// Call StartActivityForResult so we can get the app name selected by the user
this.startActivityForResult(intentPick, REQUEST_CODE_MY_PICK);
}
}
Finally, to be able to get the app selected by the user you must override the onActivityResult on your activity:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_CODE_MY_PICK) {
if(data != null && data.getComponent() != null && !TextUtils.isEmpty(data.getComponent().flattenToShortString()) ) {
String appName = data.getComponent().flattenToShortString();
// Now you know the app being picked.
// data is a copy of your launchIntent with this important extra info added.
// Start the selected activity
startActivity(data);
}
}
}
This should work for early versions of Android.
Use intent PICKER instead of CHOOSER. The difference is that picker won't start the target intent automatically, but rather, it returns to onActivityResult() the target intent with the selected app's component name attached. Then you start the target intent in the callback as a 2nd step.
A little bit of code should explain,
// In MyActivity class
static final int REQUEST_CODE_MY_PICK = 1;
// Getting ready to start intent. Note: call startActivityForResult()
... launchIntent = the target intent you want to start;
Intent intentPick = new Intent();
intentPick.setAction(Intent.ACTION_PICK_ACTIVITY);
intentPick.putExtra(Intent.EXTRA_TITLE, "Launch using");
intentPick.putExtra(Intent.EXTRA_INTENT, launchIntent);
this.startActivityForResult(intentPick, REQUEST_CODE_MY_PICK);
// You have just started a picker activity,
// let's see what user will pick in the following callback
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == REQUEST_CODE_MY_PICK) {
String appName = data.getComponent().flattenToShortString();
// Now you know the app being picked.
// data is a copy of your launchIntent with this important extra info added.
// Don't forget to start it!
startActivity(data);
}
}
I did in different way, no need to implement custom component:
Send Intent:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "My feature text");
sendIntent.setType("text/plain");
Intent receiver = new Intent(this, ApplicationSelectorReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, receiver, PendingIntent.FLAG_UPDATE_CURRENT);
Intent chooser = Intent.createChooser(sendIntent, null, pendingIntent.getIntentSender());
startActivity(chooser);
Add BroadcastReceiver ApplicationSelectorReceiver.class in manifest.
<receiver android:name=".ApplicationSelectorReceiver"></receiver>
ApplicationSelectorReceiver.java
public class ApplicationSelectorReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
for (String key : Objects.requireNonNull(intent.getExtras()).keySet()) {
try {
ComponentName componentInfo = (ComponentName) intent.getExtras().get(key);
PackageManager packageManager = context.getPackageManager();
assert componentInfo != null;
String appName = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(componentInfo.getPackageName(), PackageManager.GET_META_DATA));
Log.i("Selected Application Name", appName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Result:
Gmail
Facebook
Hangouts
Instagram
Drive
Hope this would help others.
The CommonsWare's solution only works from Android 5.1 (API level 22). Here is my solution to work with all Android versions, by creating our own app chooser dialog.
Step 1: Create a custom layout for the app chooser dialog.
dialog_app_chooser.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/text_view_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingStart="20dp"
android:paddingTop="14dp"
android:paddingEnd="0dp"
android:paddingBottom="14dp"
android:text="Select an action"
android:textColor="#000"
android:textSize="16sp"
android:textStyle="bold" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view_apps"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="16dp"
android:paddingEnd="16dp" />
</LinearLayout>
Step 2: Create a layout for an item in app chooser dialog.
item_app.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_app"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="#+id/image_view_app_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" />
<TextView
android:id="#+id/text_view_app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:textColor="#000"
android:textSize="12sp" />
</LinearLayout>
Step 3: Create a model class which indicates an app in the app chooser dialog.
App.java
public class App implements Parcelable {
public Intent intent;
public ResolveInfo resolveInfo;
public App(Intent intent, ResolveInfo resolveInfo) {
this.intent = intent;
this.resolveInfo = resolveInfo;
}
protected App(Parcel in) {
intent = in.readParcelable(Intent.class.getClassLoader());
resolveInfo = in.readParcelable(ResolveInfo.class.getClassLoader());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(intent, flags);
dest.writeParcelable(resolveInfo, flags);
}
public static final Creator<App> CREATOR = new Creator<App>() {
#Override
public App createFromParcel(Parcel in) {
return new App(in);
}
#Override
public App[] newArray(int size) {
return new App[size];
}
};
}
Step 4: Create a custom adapter which display all apps in the app chooser dialog.
AppAdapter.java
public class AppAdapter extends RecyclerView.Adapter<AppAdapter.ViewHolder> {
private List<App> apps;
private OnItemClickListener listener;
public AppAdapter(List<App> apps, OnItemClickListener listener) {
this.apps = apps;
this.listener = listener;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int position) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_app, parent, false);
return new ViewHolder(view, listener);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int position) {
App app = apps.get(viewHolder.getAdapterPosition());
viewHolder.bind(app);
}
#Override
public int getItemCount() {
return apps.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private ImageView appIcon;
private TextView appName;
private App app;
ViewHolder(View itemView, final OnItemClickListener listener) {
super(itemView);
appIcon = itemView.findViewById(R.id.image_view_app_icon);
appName = itemView.findViewById(R.id.text_view_app_name);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onItemClick(app);
}
});
}
void bind(App app) {
this.app = app;
PackageManager packageManager = appName.getContext().getPackageManager();
appIcon.setImageDrawable(app.resolveInfo.loadIcon(packageManager));
appName.setText(app.resolveInfo.loadLabel(packageManager));
}
}
interface OnItemClickListener {
void onItemClick(App app);
}
}
Step 5: Create the app chooser dialog.
AppChooserDialog.java
public class AppChooserDialog extends BottomSheetDialogFragment implements AppAdapter.OnItemClickListener {
private static final String KEY_APPS = "KEY_APPS";
private static final String KEY_TITLE = "KEY_TITLE";
private static final String KEY_REQUEST_CODE = "KEY_REQUEST_CODE";
public static void show(AppCompatActivity activity, ArrayList<Intent> targets, String title, int requestCode) {
PackageManager packageManager = activity.getPackageManager();
ArrayList<App> apps = new ArrayList<>();
for (Intent intent : targets) {
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : activities) {
Intent targetIntent = new Intent(intent);
apps.add(new App(targetIntent, resolveInfo));
}
}
if (apps.size() > 0) {
if (apps.size() == 1) {
activity.startActivityForResult(apps.get(0).intent, requestCode);
} else {
DialogFragment appChooserDialog = new AppChooserDialog();
Bundle data = new Bundle();
data.putParcelableArrayList(KEY_APPS, apps);
data.putString(KEY_TITLE, title);
data.putInt(KEY_REQUEST_CODE, requestCode);
appChooserDialog.setArguments(data);
appChooserDialog.show(activity.getSupportFragmentManager(), "AppChooserDialog");
}
}
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.dialog_app_chooser, container, false);
TextView titleTextView = rootView.findViewById(R.id.text_view_title);
RecyclerView appsRecyclerView = rootView.findViewById(R.id.recycler_view_apps);
String title = getArguments().getString(KEY_TITLE);
if (!TextUtils.isEmpty(title)) {
titleTextView.setText(title);
}
List<App> apps = getArguments().getParcelableArrayList(KEY_APPS);
appsRecyclerView.setAdapter(new AppAdapter(apps, this));
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
float screenWidthInDp = displayMetrics.widthPixels / displayMetrics.density;
int recyclerViewItemWidthInDp = 72;
int recyclerViewStartEndPadding = 32;
int numberOfColumns = (int) ((screenWidthInDp - recyclerViewStartEndPadding) / recyclerViewItemWidthInDp);
int spanCount = (apps.size() < numberOfColumns) ? apps.size() : numberOfColumns;
appsRecyclerView.setLayoutManager(new GridLayoutManager(requireActivity(), spanCount));
return rootView;
}
#Override
public void onItemClick(App app) {
ActivityInfo activity = app.resolveInfo.activityInfo;
String packageName = activity.applicationInfo.packageName;
ComponentName component = new ComponentName(packageName, activity.name);
Intent intent = new Intent(app.intent);
intent.setComponent(component);
Uri uri = app.intent.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
if (uri != null) {
requireActivity().grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
OnAppSelectedListener listener = null;
try {
listener = (OnAppSelectedListener) requireActivity();
} catch (Exception e) {
// Ignore exception
}
if (listener != null) {
listener.onAppSelected(intent);
}
requireActivity().startActivityForResult(intent, getArguments().getInt(KEY_REQUEST_CODE));
dismiss();
}
public interface OnAppSelectedListener {
void onAppSelected(Intent intent);
}
}
Step 6: Using the app chooser dialog from an activity.
public class MainActivity extends AppCompatActivity implements AppChooserDialog.OnAppSelectedListener {
private static final int REQUEST_CODE_PICK_IMAGE = 100;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<Intent> intents = new ArrayList<>();
Intent pickImageIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intents.add(pickImageIntent);
AppChooserDialog.show(this, intents, "Pick image from", REQUEST_CODE_PICK_IMAGE);
}
#Override
public void onAppSelected(Intent intent) {
ComponentName componentName = intent.getComponent();
String packageName = componentName.getPackageName();
String activityName = componentName.getClassName();
Log.i("TAG", "packageName = " + packageName + ", activityName = " + activityName);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_PICK_IMAGE) {
Log.i("TAG", "onActivityResult");
// TODO: Put your logic here.
}
}
}
The result:
Related
I got an email from google play support saying "Intent Redirection Your app(s) are vulnerable to Intent Redirection. To address this issue, follow the steps in this Google Help Center article."
After reading through the article, I'm guessing the key is my app should not call startActivity, startService, sendBroadcast, or setResult on untrusted Intents (intents used by external apps to invoke my app for example) without validating or sanitizing these Intents.
However, solution 1 in the article doesn't work in my case because my component needs to receive Intents from other apps.
Solution 2 is not applicable to my case because I don't know in advance which app would invoke my app, so I don't know what would getCallingActivity returns.
Solution 3 seems to be the most promising one, I tried to removeFlags of intents, however, when I resubmit my app, Google Play again alerts this vulnerability. I am about to try checking whether an Intent grants a URI permission using methods like getFlags and submit my app again to see the result. Does anyone know how do Google check this vulnerability anyway, and could someone spot the vulnerability in my source code and suggest a way to resolve it?
The exact message from Google Play is
Intent Redirection
Your app(s) are vulnerable to Intent Redirection.
To address this issue, follow the steps in this Google Help Center article.
com.mydomain.somepackage.a->a
And the following is the simplified source code.
// MainActivity.java
public class MainActivity extends CordovaActivity
{
SpecialUtil specialUtil;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
specialUtil = new specialUtil(MainActivity.this);
}
#Override
public void onResume() {
super.onResume();
specialUtil.verifyServerIfNeeded(MainActivity.this);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == this.specialUtil.CERT_INVALID_POPUP_REQUEST_CODE) {
// the user clicked the return button in the alert dialog within WhiteScreen activity
this.specialUtil.declareAsFailure();
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
}
// com/mydomain/somepackage/SpecialUtil.java
public class SpecialUtil {
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mSharedPreferencesEditor;
private SharedPreferences.OnSharedPreferenceChangeListener listener;
private Activity activity;
private boolean shownCertInvalidPopup = false;
public final int CERT_INVALID_POPUP_REQUEST_CODE = 1000;
public SpecialUtil(Activity activity) {
this.activity = activity;
this.mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
this.mSharedPreferencesEditor = mSharedPreferences.edit();
this.listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("SOME_RESULT")) {
String result = mSharedPreferences.getString("SOME_RESULT", "");
if (result.equals("RESULT_OK")) {
SpecialUtil.this.declareAsSuccess();
} else if (result.equals("RESULT_CANCELED")) {
SpecialUtil.this.declareAsFailure();
}
}
}
};
this.mSharedPreferences.registerOnSharedPreferenceChangeListener(listener);
}
public void verifyServerIfNeeded(Activity activity) {
Intent intent = activity.getIntent();
if (this.isFlowA(intent)) {
this.removePermission(intent);
String url = intent.getStringExtra("url");
this.verifyServer(url);
} else if (this.isFlowB(intent)) {
this.removePermission(intent);
String payment_request_object_url = intent.getData().getQueryParameter("pay_req_obj");
String callback_url = intent.getData().getQueryParameter("callback");
this.verifyServer(payment_request_object_url);
}
}
public boolean isFlowA(Intent intent) {
if (intent.getAction().equals("someAction")) {
return true;
}
return false;
}
public boolean isFlowB(Intent intent) {
if (intent.getData() != null) {
String path = intent.getData().getPath();
if (path.equals("something")) {
return true;
}
}
return false;
}
public void verifyServer(final String httpsURL) {
new Thread(new Runnable() {
#Override
public void run() {
try {
boolean isCertValid = SpecialUtil.this.verify(httpsURL);
if (isCertValid) {
// do somthing
} else {
// show a white screen with an alert msg
SpecialUtil.this.activity.runOnUiThread(new Runnable() {
public void run() {
if (!shownCertInvalidPopup) {
shownCertInvalidPopup = true;
Intent intent = new Intent(SpecialUtil.this.activity, WhiteScreen.class);
SpecialUtil.this.activity.startActivityForResult(intent, CERT_INVALID_POPUP_REQUEST_CODE);
}
}
});
}
} catch (IOException e) {
SpecialUtil.this.declareAsFailure();
}
}
}).start();
}
private void declareAsSuccess() {
this.activity.setResult(Activity.RESULT_OK, SpecialUtil.this.activity.getIntent());
this.activity.finishAndRemoveTask();
}
public void declareAsFailure() {
this.activity.setResult(Activity.RESULT_CANCELED, this.activity.getIntent());
this.activity.finishAndRemoveTask();
}
private void removePermission(Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.removeFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.removeFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
// com/mydomain/somepackage/WhiteScreen.java
public class WhiteScreen extends Activity {
SpecialUtil specialUtil;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
specialUtil = new SpecialUtil(WhiteScreen.this);
String title = "someTitle";
final AlertDialog.Builder builder = new AlertDialog.Builder(WhiteScreen.this)
.setTitle(title)
.setPositiveButton(btn_text, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Don't start the process, quit App immediately
WhiteScreen.this.setResult(Activity.RESULT_CANCELED, WhiteScreen.this.getIntent());
WhiteScreen.this.finishAndRemoveTask();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
}
There are a list of students.each row shows an Image,Name and number.
I created a Room database and only managed to populate "name" and "number" columns into the list using this guide.
When user opens the AddNewStudentActivity, He/She needs to choose a photo from gallery and fill two editTexts for the name and number and click "save" and save to student to the studentDatabase.
the image should be displayed in the list alongside those two texts(name and number).
I have NO IDEA how to do this i only think the process should be like "setting up an intent that opens the gallery and we can choose an image and get it's path stored on the database and display it from database to the list"but don't know how to code it.there are tutorials about this but they were all using SQLITE and not Room and I'm new to the whole database topic.
-Thanks
NewStudentActivity.java
public class NewStudentActivity extends AppCompatActivity {
public static final String EXTRA_REPLY = "com.example.android.studentlistsql.REPLY";
private EditText mNameWordView;
private EditText mNumWordView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_student);
mNameWordView = findViewById(R.id.name_word);
mNumWordView = findViewById(R.id.num_word);
final Button button = findViewById(R.id.button_save);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent replyIntent = new Intent();
if (TextUtils.isEmpty(mNameWordView.getText())) {
setResult(RESULT_CANCELED, replyIntent);
} else {
String word = mNameWordView.getText().toString();
replyIntent.putExtra(EXTRA_REPLY, word);
setResult(RESULT_OK, replyIntent);
}
if (TextUtils.isEmpty(mNumWordView.getText())) {
setResult(RESULT_CANCELED, replyIntent);
} else {
String word = mNumWordView.getText().toString();
replyIntent.putExtra(EXTRA_REPLY, word);
setResult(RESULT_OK, replyIntent);
}
finish();
}
});
}
}
StudentListAdapter.java
public class StudentListAdapter extends RecyclerView.Adapter<StudentListAdapter.WordViewHolder> {
class WordViewHolder extends RecyclerView.ViewHolder {
private final TextView nameItemView;
private final TextView numberItemView;
private WordViewHolder(View itemView) {
super(itemView);
nameItemView = itemView.findViewById(R.id.nameTextView);
numberItemView = itemView.findViewById(R.id.numberTextView);
}
}
private final LayoutInflater mInflater;
private List<Student> mStudents; // Cached copy of words
StudentListAdapter(Context context) { mInflater = LayoutInflater.from(context); }
#Override
public WordViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = mInflater.inflate(R.layout.recyclerview_item, parent, false);
return new WordViewHolder(itemView);
}
#Override
public void onBindViewHolder(WordViewHolder holder, int position) {
if (mStudents != null) {
Student current = mStudents.get(position);
holder.nameItemView.setText(current.getStudentName());
holder.numberItemView.setText(current.getStudentNumber());
} else {
// Covers the case of data not being ready yet.
holder.nameItemView.setText("No Word");
}
}
void setStudents(List<Student> words){
mStudents = words;
notifyDataSetChanged();
}
// getItemCount() is called many times, and when it is first called,
// mWords has not been updated (means initially, it's null, and we can't return null).
#Override
public int getItemCount() {
if (mStudents != null)
return mStudents.size();
else return 0;
}
}
To be honest the process isn't that much different with Room. As you said, to pick the photo from the Gallery you use an intent:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "select a picture"), YOUR_IMAGE_CODE);
Then you handle this case in onActivityResult:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == YOUR_IMAGE_CODE) {
if(resultCode == RESULT_OK)
Uri selectedImageUri = data.getData();
}
}
So selectedImageUri is the piece of information that you save in your database. In your Entity class Student.java you can change mStudentPic to String so when you insert your Uri you can use a method:
selectedImageUri.toString();
and when you want to convert it back to Uri:
Uri uri = Uri.parse(yourUriAsString);
I assumed that you know how to insert and query values from the database.
And then in your onBindViewHolder you can use Glide or Picasso, or any other library to load the image, for example with Glide:
Glide.with(context)
.load(new File(uri.getPath()))
.into(imageView);
Also, I am using the same function with SQLite I am the storing just image path and get from SQLite and load image using image URI
holder.imgUserPhoto.setImageURI(Uri.parse(new File(contactModel.getContactPhoto()).toString()));
I am implementing custom turn-by-turn navigation in my android application. To achieve this, I have started the activity from my MainActivity using an intent which uses Intent.ACTION_VIEW as action and "google.navigation:q" as uri string.The google maps navigation page is successfully loaded in my app.
But, I don't know how to gracefully exit from this page. If I use back button press, it takes 4 back button clicks to display my main activity screen. Is there any possibility to place "exit" button in this page.
I have tried "onActivityForResult" and "onBackPressed" for destroying the google maps screens. None of this works. Please provide some suggestions to go further.
I know I am pretty late to answer this but maybe it can help someone.
You cannot come back from google map to your activity/app on single back press for this you need to create a floating view/widget like ola/uber which will do this for you after proper implementation. Here is my implementation.
First the user will go to map app from YourActivity. In this activity we will ask the permission for SYSTEM_ALERT_WINDOW (DRAW OVER, for SDK > MarshMallow) on click of some view. Then we will launch google map as well as a Service created by us to create a floating icon.
class YourActivity extends AppCompatActivity{
private GetFloatingIconClick mGetServiceClick;
public static boolean isFloatingIconServiceAlive = false;
onCreate(){
mGetServiceClick = new GetFloatingIconClick();
somebtn.onclick(){
askDrawOverPermission();
}
}
private class GetFloatingIconClick extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent selfIntent = new Intent(YourActivity.this, YourActivity.class);
selfIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(selfIntent);
}
}
private void askDrawOverPermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// if OS is pre-marshmallow then create the floating icon, no permission is needed
createFloatingBackButton();
} else {
if (!Settings.canDrawOverlays(this)) {
// asking for DRAW_OVER permission in settings
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getApplicationContext().getPackageName()));
startActivityForResult(intent, REQ_CODE_DRAW_OVER);
} else {
createFloatingBackButton();
}
}
}
// starting service for creating a floating icon over map
private void createFloatingBackButton() {
Intent iconServiceIntent = new Intent(YourActivity.this, FloatingOverMapIconService.class);
iconServiceIntent.putExtra("RIDE_ID", str_rideId);
Intent navigation = new Intent(Intent.ACTION_VIEW, Uri
.parse("google.navigation:q=" + lat_DEST + "," + lng_DEST + "&mode=d"));
navigation.setPackage("com.google.android.apps.maps");
startActivityForResult(navigation, 1234);
startService(iconServiceIntent);
}
#TargetApi(Build.VERSION_CODES.M)
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQ_CODE_DRAW_OVER) {
// as permissions from Settings don't provide any callbacks, hence checking again for the permission
// so that we can draw our floating without asking user to click on the previously clicked view
// again
if (Settings.canDrawOverlays(this)) {
createFloatingBackButton();
} else {
//permission is not provided by user, do your task
//GlobalVariables.alert(mContext, "This permission is necessary for this application's functioning");
}
} else if (requestCode == 1234) {
// no result is returned by google map, as google don't provide any apis or documentation
// for it.
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
Service Class:-
public class FloatingOverMapIconService extends Service {
private WindowManager windowManager;
private FrameLayout frameLayout;
private String str_ride_id;
public static final String BROADCAST_ACTION = "com.yourpackage.YourActivity";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
createFloatingBackButton();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// to receive any data from activity
str_ride_id = intent.getStringExtra("RIDE_ID");
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
windowManager.removeView(frameLayout);
}
private void createFloatingBackButton() {
CurrentJobDetail.isFloatingIconServiceAlive = true;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
frameLayout = new FrameLayout(this);
LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
// Here is the place where you can inject whatever layout you want in the frame layout
layoutInflater.inflate(R.layout.custom_start_ride_back_button_over_map, frameLayout);
ImageView backOnMap = (ImageView) frameLayout.findViewById(R.id.custom_drawover_back_button);
backOnMap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(BROADCAST_ACTION);
intent.putExtra("RIDE_ID", str_ride_id);
sendBroadcast(intent);
//stopping the service
FloatingOverMapIconService.this.stopSelf();
CurrentJobDetail.isFloatingIconServiceAlive = false;
}
});
windowManager.addView(frameLayout, params);
}
}
Floating Icon Xml:-
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/custom_drawover_back_button"
android:layout_width="70dp"
android:layout_height="100dp"
android:src="#drawable/common_full_open_on_phone"
android:scaleType="center"
android:background="#color/colorAccent"/>
</LinearLayout>
Manifest file :-
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<activity
android:name=".Activities.YourActivity"
android:launchMode="singleTop" />
<service
android:name=".Utils.FloatingOverMapIconService"
android:exported="false" />
I'm working from Unity and want to get the result of a file picking activity. Since I can't start activities from Unity itself, I created an "intermediant" static class called EclipseIntegration:
package com.Unity3D.EclipseIntegration;
public class EclipseIntegration {
public static String selectedPath = "really ";
public static void Launch(Activity activity) {
Intent target = new Intent(activity, MyActivity.class);
activity.startActivity(target);
}
}
activity is the Activity Unity is currently running in.
MyActivity is meant to start the real activity, because I need something that inherits from Activity to get the onActivityResult event:
package com.Unity3D.EclipseIntegration;
public class MyActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Launch();
}
public void Launch() {
Intent target = FileUtils.createGetContentIntent();
Intent intent = Intent.createChooser(target, "Select a movie");
startActivityForResult(intent);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 128:
if (resultCode == RESULT_OK) {
final Uri uri = data.getData();
File file = FileUtils.getFile(uri);
EclipseIntegration.selectedPath = file.getAbsolutePath();
}
else EclipseIntegration.selectedPath = "";
finish();
}
}
}
but nothing happens. If I start the final activity directly in the static class like this :
activity.startActivity(intent); it works though, so I suppose I made some error while setting up the MyActivity? Despite trying this for 2 days now I couldn't find it though, so any nudge in the right direction would be very appreciated :)
Thanks,
kiriri
Try this way
public void Launch() {
Intent target = FileUtils.createGetContentIntent();
Intent intent = Intent.createChooser(target, "Select a movie");
startActivityForResult(intent,128);
}
I am trying to get the response from activate device admin by user.
if the user press activate then do somethings or if the user press cancel a dialog will pop to inform user should activate.
i try to use
In Class DeviceAdmin
public class DeviceAdmin {
public static final int DEVICE_ADMIN_REQUEST = 9;
public static final int START_SERVICE = 8;
private static DevicePolicyManager mDevicePolicyManager;
private static ComponentName mComponentName;
public static DevicePolicyManager getDevicePolicyManager() {
return mDevicePolicyManager;
}
public static void setDevicePolicyManager(
final DevicePolicyManager devicePolicyManager) {
mDevicePolicyManager = devicePolicyManager;
}
public static ComponentName getComponentName() {
return mComponentName;
}
public static void setComponentName(final ComponentName componentName) {
mComponentName = componentName;
}
public static void initDPM(final Activity activity) {
if (mDevicePolicyManager == null) {
setDevicePolicyManager((DevicePolicyManager) activity
.getSystemService(Context.DEVICE_POLICY_SERVICE));
}
}
public static <T> void initComponent(final Activity activity,
final Class<T> reciever) {
if (mComponentName == null) {
setComponentName(new ComponentName(activity, reciever));
}
}
public static boolean isDeviceAdmin() {
return mDevicePolicyManager.isAdminActive(mComponentName);
}
public static void registerDeviceAdmin(final Activity activity,
final int requestCode) {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
getComponentName());
activity.startActivityForResult(intent, requestCode);
}
public static void unregisterDeviceAdmin() {
if (mDevicePolicyManager.isAdminActive(mComponentName))
mDevicePolicyManager.removeActiveAdmin(mComponentName);
}
}
In MainActivity
public class LockIt extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
if (!DeviceAdmin.isDeviceAdmin())
DeviceAdmin.registerDeviceAdmin(this,
DeviceAdmin.DEVICE_ADMIN_REQUEST);
else
// Do something;
}
private void init() {
DeviceAdmin.initDPM(this);
DeviceAdmin.initComponent(this, LockItNowAdminReceiver.class);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DeviceAdmin.DEVICE_ADMIN_REQUEST) {
if (resultCode == RESULT_OK) {
// do something;
} else if (resultCode == RESULT_CANCELED) {
// showAdminDialog;
}
}
}
}
RESULT_OK is -1, RESULT_CANCELED is 0
But when I ran in debug mode, I found that no matter user press Cancel or Activate,
resultCode is always 0.
UPDATE
OKAY, I think I have found the reason. I used android:launchMode="singleInstance" in Manifest of MainActivity
Do this is a proper way to get the response of device admin activation?
Or I have do something wrong?
Yes, that's true, but you should put an instance of a class extending DeviceAdminReceiver:
ComponentName mDeviceAdminSample = new ComponentName(v.getContext(), DeviceAdmin.class);
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdminSample);
activity.startActivityForResult(intent, DEVICE_ADMIN_REQUEST);
And in onAcitivityResult:
if (requestCode == DEVICE_ADMIN_REQUEST}
if(resultCode == RESULT_OK) {
// admin
} else {
// not admin
}
}
A complete sample can be found here
Just get sure of declaring your receiver in the AndroidManifest.XML.
Finally
I think I have found the reason.
I used android:launchMode="singleInstance" in Manifest of MainActivity
Now I use android:taskAffinity.
It seems everything okay now.