According to the Android documentation if I don't want my ShareActionProvider to persist the share history I should call
mShareActionProvider.setShareHistoryFileName(null)
However when I do this I get the following crash on selecting a share option:
11-15 10:06:34.848: E/AndroidRuntime(22461): java.lang.IllegalStateException: No preceding call to #readHistoricalData
11-15 10:06:34.848: E/AndroidRuntime(22461): at android.widget.ActivityChooserModel.persistHistoricalDataIfNeeded(ActivityChooserModel.java:573)
11-15 10:06:34.848: E/AndroidRuntime(22461): at android.widget.ActivityChooserModel.addHisoricalRecord(ActivityChooserModel.java:743)
11-15 10:06:34.848: E/AndroidRuntime(22461): at android.widget.ActivityChooserModel.chooseActivity(ActivityChooserModel.java:491)
11-15 10:06:34.848: E/AndroidRuntime(22461): at android.widget.ActivityChooserView$Callbacks.onItemClick(ActivityChooserView.java:547)
Here is the code that sets up the ShareActionProvider:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.article_pager_menu, menu);
// mShareActionProvider is a field in the Activity
mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
.getActionProvider();
mShareActionProvider
.setShareHistoryFileName(null);
Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
shareIntent.setType("text/plain");
mShareActionProvider.setShareIntent(shareIntent);
mShareActionProvider.onCreateActionView();
return true;
}
Any ideas how I can fix this?
So in the end I had to write my own ShareActionProvider by copying the one found in Android source. I also had to copy over the ActivityChooserView and the ActivityChooserModel from source. The actual modification needed to hide the default activity in the action bar is in the updateAppearance() method in the ActivityChooserView. This is how it should look:
private void updateAppearance() {
// Expand overflow button.
if (mAdapter.getCount() > 0) {
mExpandActivityOverflowButton.setEnabled(true);
} else {
mExpandActivityOverflowButton.setEnabled(false);
}
mDefaultActivityButton.setVisibility(View.GONE);
if (mDefaultActivityButton.getVisibility() == VISIBLE) {
mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground);
} else {
mActivityChooserContent.setBackgroundDrawable(null);
}
}
I couldn't figure out why setShareHistoryFileName(null) was causing the problem I originally described though. Thanks for the attempted answer Seven.
Reading the source code on ActivityChooserModel I've found that the history file is open using Context's openFileInput. As long as that class keep working like that, you will be able to keep your history "clean" if you delete it using the usual method for those kinds of files:
getApplicationContext().deleteFile(SHARE_HISTORY_FILE_NAME);
shareActionProvider.setShareHistoryFileName(SHARE_HISTORY_FILE_NAME);
The "most used" icon will show for a while, when the selected application is opening, but as soon as the user is back at your app, it will disappear.
You could also delete the file on your onShareTargetSelected method, if needed.
I tried all, I'm using old widget.ShareActionProvider (not compat 7), so null leads to crash, deleteFile surely deletes but history always still exists after app restart... so I've found only one working thing: random!
String fname=ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME;
try {
fname = prefs.getString("SHARE_HISTORY_FILE_NAME", ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
getApplicationContext().deleteFile(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
fname="SHARE_HISTORY_FILE_NAME"+Math.random()*1000;
SharedPreferences.Editor ed = prefs.edit();
ed.putString("SHARE_HISTORY_FILE_NAME", fname);
ed.commit();
} catch (Exception e) {
Log.e(TAG,"err "+e.toString());
}
mSharedActionProvider.setShareHistoryFileName(fname);
modified com.actionbarsherlock.widget.ShareActionProvider's onCreateActionView then call ActivityChooserModel's method: setHistoryMaxSize(0)
Related
I have a list of package name that executed my app.
and the list is like this.
android (?)
com.gau.go.launcherex
android (?)
I confused I never seen this package name (android).
anyone has idea? thx for help.
here is the code to make list of package name
Uri ref = getReferrer();
if (ref != null) {
String host = ref.getHost();
if (host != null && !host.equals("")) {
boolean isWhite = false;
for (String item : whitelist) {
if (item.equals(host)) {
isWhite = true;
break;
}
}
if (!isWhite) {
// add to list
}
}
}
which package you've never seen? can you post more complete log and code which logged these lines? com.gau.go.launcherex is available HERE in Google Play, this is device launcher (what is launcher in HERE). and simple android package may mean app is bring-back from background using some system option, e.g. recents button/menu
edit due code posted in question - some suggestions: instead of for loop just use whiteList.contains(host), if you need to null/empty check host String then use TextUtils.isEmpty
In a blog app, I have two activities Feed and single post activity.
Feed activities show all the posts and when user click on any post it opens in a single post activity.
In single post activity, I have menu giving the option to edit and delete the post.
On deleting, the post should be deleted and intent should take the user to feed activity but what happens is, post get deleted but intent doesn't execute and it shows the error given below.
Here is the code I wrote ithe n menu option
#Override
// Action on selecting the menu
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_edit) {
// Add code to edit the post
} else if (item.getItemId() == R.id.action_delete) {
// Condition to test if the user who is deleting the post is the same who posted it
if (PostUserName.getText().toString().equals(userName)) {
// postkey is parent, which contains data of that single post
dbref.child(postKey).removeValue();
Intent toFeed = new Intent(SinglePostActivity.this,
FeedActivity.class);
startActivity(toFeed);
} else {
Toast.makeText(this, "Can't delete", Toast.LENGTH_SHORT).show();
}
}
return super.onOptionsItemSelected(item);
}
Error Displayed
11-20 16:20:48.808 1960-1979/? E/Auth: [GoogleAccountDataServiceImpl] getToken() -> BAD_AUTHENTICATION. Account: , App: com.android.vending, Service: androidmarket
edx: Long live credential not available.
at edy.b(:com.google.android.gms#11518470:10)
at edy.a(:com.google.android.gms#11518470:50)
at ecl.a(:com.google.android.gms#11518470:50)
at flk.a(:com.google.android.gms#11518470:8)
at flk.a(:com.google.android.gms#11518470:4)
at fkj.a(:com.google.android.gms#11518470:2)
at fkh.a(:com.google.android.gms#11518470:17)
at fkh.a(:com.google.android.gms#11518470:6)
at bsr.a(:com.google.android.gms#11518470:203)
at bsr.a(:com.google.android.gms#11518470:61)
at dzj.a(:com.google.android.gms#11518470:6)
at dzi.a(:com.google.android.gms#11518470:2)
at dzi.e(:com.google.android.gms#11518470:6)
at dzh.a(:com.google.android.gms#11518470:1)
at eax.getAuthToken(:com.google.android.gms#11518470:7)
at android.accounts.AbstractAccountAuthenticator$Transport.getAuthToken(AbstractAccountAuthenticator.java:244)
at android.accounts.IAccountAuthenticator$Stub.onTransact(IAccountAuthenticator.java:113)
at android.os.Binder.transact(Binder.java:499)
at buy.onTransact(:com.google.android.gms#11518470:3)
at android.os.Binder.execTransact(Binder.java:565)
UPDATED
Solved the problem by deleting my account info from the emulator and putting it again then by updating google play services and google play itself.
But, Now getting Null Pointer Exception at line 125 , may be because the single post has been deleted and now single post activity has nothing to display. I know this is very basic but I'm new to coding and don't know how to tackle it. here is the code,
String postUserName = (String) dataSnapshot.child("userId").getValue();
String postTitle = (String) dataSnapshot.child("title").getValue();
String postDesc = (String) dataSnapshot.child("desc").getValue();
String postImage = (String) dataSnapshot.child("image").getValue();
// line 125, which showed error.
// and I would also like to know why the above String did not show the same error
long postupVoteCount = (long) dataSnapshot.child("upVote").getValue();
int upVotes = (int) postupVoteCount;
long postDownVoteCount = (long) dataSnapshot.child("downVote").getValue();
int downVotes = (int) postDownVoteCount;
PostUserName.setText(postUserName);
PostTitle.setText(postTitle);
PostDesc.setText(postDesc);
PostUpVoteCount.setText(Integer.toString(upVotes));
PostDownVoteCount.setText(Integer.toString(downVotes));
Please Help.
The Problem
On Android versions < 4.1, the alpha value of the MenuItem is getting reset after an orientation change, BUT it remains disabled.
The code I'm using
DetailsFragment.java
public class DetailsFragment extends SherlockFragment {
private MenuItem miEmail;
...
#Override
public void onPrepareOptionsMenu(Menu menu) {
miEmail= menu.findItem(R.id.menu_email);
super.onPrepareOptionsMenu(menu);
}
private void populateDetails(final Detail oDetail) {
//disable email button if dealer doesn't have option
if(!oDetail.bShowSAM){
miEmail.setEnabled(false);
miEmail.getIcon().setAlpha(50);
}
...
}
}
MyManifest.xml
<activity
android:name=".activities.DetailsActivity"
android:uiOptions="splitActionBarWhenNarrow"
android:configChanges="keyboardHidden|screenSize|orientation">
</activity>
What I expect to happen
When the orientation changes, miEmail is still disabled and the alpha value is still at 50.
What is actually happening
When testing on older devices(2.3,4.0), the MenuItem is remaining disabled but the alpha value is getting reset to the default value. When testing with my devices that are >4.1, it is working as expected.
What I've tried
Googling the problem.......
I've tried to avoid using the android:configChanges="..." and handling the data through savedInstanceState, but I've learned you can't make the MenuItem serializable/parciable, thus not allowing me to pass it through outState bundle object.
I'm fairly new to Android development and I feel as though there is a trivial way of handling this MenuItem, but I cannot figure how else to handle it.
What do you think is the issue?
Any feedback will be greatly appreciated.
Dont set the icon alpha on your custom function, instead, set it on OnPrepareOptionsMenu (with a suitable conditional). You can pass a boolean on savedinstancestate saying whether it should be grayed or not.
In your populateDetails function, you would call invalidateOptionsMenu() to make android remake the action bar icons. Example:
private boolean buttonEnabled;
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem miEmail= menu.findItem(R.id.menu_email);
if (buttonEnabled) {
miEmail.setEnabled(true);
miEmail.getIcon().setAlpha(255);
}else{
miEmail.setEnabled(true);
miEmail.getIcon().setAlpha(50);
}
return super.onPrepareOptionsMenu(menu);
}
private void populateDetails(final Detail oDetail) {
//disable email button if dealer doesn't have option
if(!oDetail.bShowSAM){
buttonEnabled = false;
InvalidateOptionsMenu();
}
...
}
}
If you are using the support library for compatibility, use supportInvalidateOptionsMenu instead.
By the way, never use the orientation tag to "fix" the problem, the issue will still appear if you quit the app for a long time and then try to open it. (android pauses the activity initially and will stop it after a while)
I developed an application that contains a homescreen with an article list.
If you click on it, you access the detail in another screen.
I implemented the ActionBarSherlock, so I used the "up" button pattern for this activity.
Then I added a widget to this application. When you click on the widget, you access directly the detail activity.
The "up" button has been implemented following the Google recommandations (http://developer.android.com/training/implementing-navigation/ancestral.html).
My problem is that on API Level 15 and below, it works perfectly. It calls the following code :
#Override
public boolean shouldUpRecreateTask(Activity activity, Intent targetIntent) {
String action = activity.getIntent().getAction();
return action != null && !action.equals(Intent.ACTION_MAIN);
}
But on JellyBean, the code used is :
public boolean shouldUpRecreateTask(Intent targetIntent) {
try {
PackageManager pm = getPackageManager();
ComponentName cn = targetIntent.getComponent();
if (cn == null) {
cn = targetIntent.resolveActivity(pm);
}
ActivityInfo info = pm.getActivityInfo(cn, 0);
if (info.taskAffinity == null) {
return false;
}
return !ActivityManagerNative.getDefault().targetTaskAffinityMatchesActivity(mToken, info.taskAffinity);
} catch (RemoteException e) {
return false;
} catch (NameNotFoundException e) {
return false;
}
}
The first part of the method retrieves information on the activity that should be loaded if stack must be recreated.
But I still don't understand what does the line :
!ActivityManagerNative.getDefault().targetTaskAffinityMatchesActivity(mToken, info.taskAffinity);
Can anyone help me on this line, I really need to find out how to obtain true by initializing everything well ?
Its a boolean method it has to return something. If it needs to return a true boolean variable for it work, you have to do so!
From the official Documentation:
Returns true if the app should recreate the task when navigating 'up'
from this activity by using targetIntent.
If this method returns false the app can trivially call navigateUpTo(Intent)
using the same parameters to correctly perform up navigation.
If this method returns false, the app should synthesize a new task stack by using
TaskStackBuilder or another similar mechanism to perform up navigation.
The affinity indicates which task an activity prefers to belong to. By default, all the activities from the same application have an affinity for each other. So, by default, all activities in the same application prefer to be in the same task. However, you can modify the default affinity for an activity. Activities defined in different applications can share an affinity, or activities defined in the same application can be assigned different task affinities.
If I create an app that depends on another app or apps (eg: the Facebook and Twitter apps), yet they are not installed, is there a method of checking for those dependencies and installing them at the same time as my own app?
I did this in my application which requires the zxing scanner app to be installed.
You will want this inside your onclick or ontouch:
try{
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.setPackage("com.google.zxing.client.android");
startActivityForResult(intent, 0);
} catch (Exception e) {
createAlert("Barcode Scanner not installed!", "This application uses " +
"the open source barcode scanner by ZXing Team, you need to install " +
"this before you can use this software!", true);
}
which calls
public void createAlert(String title, String message, Boolean button) {
// http://androidideasblog.blogspot.com/2010/02/how-to-add-messagebox-in-android.html
AlertDialog alertDialog;
alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
if ((button == true)) {
alertDialog.setButton("Download Now",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
Intent browserIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("market://search?q=pname:com.google.zxing.client.android"));
startActivity(browserIntent);
}
});
}
alertDialog.show();
}
Then after sorting out all that code out I realise you asked for it to be installed at the same time as your app. Not sure if i should post this code, but it may be helpful
Short answer: No, you cannot automatically install other applications as dependencies.
Longer answer:
Android Market does not let you declare other applications to install as a dependency. As a system, Market appears to be designed for single application installs -- not Linux distro style mega dependency graphs.
At runtime, you can test for installed apps and punt your user over to the Market if so. See the techniques suggested by #QuickNick (testing if an app is installed) and #TerryProbert (punting to market) if that's what you want.
Your best bet is probably to design your app to gracefully degrade if dependencies are not available, and suggest (or insist) that they head over to market to install them.
Start from this:
Intent mediaIntent = new Intent("com.example.intent.action.NAME");
// add needed categories
List<ResolveInfo> listResolveInfo = getPackageManager().queryIntentServices(mediaIntent, 0);
if (listResolveInfo.size() != 0) {
//normal behavior
} else {
//install what you need
}
I give you example of querying services. If you want to check activities, then you will call queryIntentActivities().
I think following the pattern outlined in this post on the Android Developer Blog will help you.
http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
As TerryProbert points out if you know that the Intent is not available prompt the user to install the missing app.
Here's what I use to return the first mission activity that exists:
try {
Class<?> missionClass = Class.forName(mPackageName+".Mission"+mission);
Method missionDescription;
missionDescription = missionClass.getMethod("missionDescription");
mMissionDescription = (String) missionDescription.invoke(null);
if (mMissionDescription.length() > 0) {
nextMission = mission;
break;
}
} catch (Exception e) {
//DEBUG*/Log.v(this.getClass().getName(), "onResume: Mission no "+mission+" not found: "+e.getMessage());
}
Each mission is held in a separate class, derived from a Mission base class. Derived classes are called Mission1, Mission24 etc.
Not all missions are defined.
The base class has an abstract class missionDescription which returns a string describing the mission.
This code is inside a loop so tests mission=1 to 99, trying to call missionDescription. It returns when the Description for the first mission found is returned.