In Android is it possible for a background service to access the url of the web page a user is currently looking at in their Chrome browser? From what ive read so far this is not possible without root access. Im just asking to see if anyone knows if maybe there is some other standard way of doing it?
No, it is not possible if you're not a Chrome developer.
yes you can.
you can use accessibility service
note: if you're not an accessibility app, Google Play will not allow you on the app store if you use this API. If you're writing a personal app its fine.
example:
How to get url from browser using accessibility service
The implementation:
public class UrlInterceptorService extends AccessibilityService {
private HashMap<String, Long> previousUrlDetections = new HashMap<>();
#Override
protected void onServiceConnected() {
AccessibilityServiceInfo info = getServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
info.packageNames = packageNames();
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_VISUAL;
//throttling of accessibility event notification
info.notificationTimeout = 300;
//support ids interception
info.flags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS |
AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
this.setServiceInfo(info);
}
private String captureUrl(AccessibilityNodeInfo info, SupportedBrowserConfig config) {
List<AccessibilityNodeInfo> nodes = info.findAccessibilityNodeInfosByViewId(config.addressBarId);
if (nodes == null || nodes.size() <= 0) {
return null;
}
AccessibilityNodeInfo addressBarNodeInfo = nodes.get(0);
String url = null;
if (addressBarNodeInfo.getText() != null) {
url = addressBarNodeInfo.getText().toString();
}
addressBarNodeInfo.recycle();
return url;
}
#Override
public void onAccessibilityEvent(#NonNull AccessibilityEvent event) {
AccessibilityNodeInfo parentNodeInfo = event.getSource();
if (parentNodeInfo == null) {
return;
}
String packageName = event.getPackageName().toString();
SupportedBrowserConfig browserConfig = null;
for (SupportedBrowserConfig supportedConfig: getSupportedBrowsers()) {
if (supportedConfig.packageName.equals(packageName)) {
browserConfig = supportedConfig;
}
}
//this is not supported browser, so exit
if (browserConfig == null) {
return;
}
String capturedUrl = captureUrl(parentNodeInfo, browserConfig);
parentNodeInfo.recycle();
//we can't find a url. Browser either was updated or opened page without url text field
if (capturedUrl == null) {
return;
}
long eventTime = event.getEventTime();
String detectionId = packageName + ", and url " + capturedUrl;
//noinspection ConstantConditions
long lastRecordedTime = previousUrlDetections.containsKey(detectionId) ? previousUrlDetections.get(detectionId) : 0;
//some kind of redirect throttling
if (eventTime - lastRecordedTime > 2000) {
previousUrlDetections.put(detectionId, eventTime);
analyzeCapturedUrl(capturedUrl, browserConfig.packageName);
}
}
private void analyzeCapturedUrl(#NonNull String capturedUrl, #NonNull String browserPackage) {
String redirectUrl = "your redirect url is here";
if (capturedUrl.contains("facebook.com")) {
performRedirect(redirectUrl, browserPackage);
}
}
/** we just reopen the browser app with our redirect url using service context
* We may use more complicated solution with invisible activity to send a simple intent to open the url */
private void performRedirect(#NonNull String redirectUrl, #NonNull String browserPackage) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl));
intent.setPackage(browserPackage);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackage);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
catch(ActivityNotFoundException e) {
// the expected browser is not installed
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl));
startActivity(i);
}
}
#Override
public void onInterrupt() { }
#NonNull
private static String[] packageNames() {
List<String> packageNames = new ArrayList<>();
for (SupportedBrowserConfig config: getSupportedBrowsers()) {
packageNames.add(config.packageName);
}
return packageNames.toArray(new String[0]);
}
private static class SupportedBrowserConfig {
public String packageName, addressBarId;
public SupportedBrowserConfig(String packageName, String addressBarId) {
this.packageName = packageName;
this.addressBarId = addressBarId;
}
}
/** #return a list of supported browser configs
* This list could be instead obtained from remote server to support future browser updates without updating an app */
#NonNull
private static List<SupportedBrowserConfig> getSupportedBrowsers() {
List<SupportedBrowserConfig> browsers = new ArrayList<>();
browsers.add( new SupportedBrowserConfig("com.android.chrome", "com.android.chrome:id/url_bar"));
browsers.add( new SupportedBrowserConfig("org.mozilla.firefox", "org.mozilla.firefox:id/url_bar_title"));
return browsers;
}
}
and accessibility service config:
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/accessibility_service_description"
android:canRetrieveWindowContent="true"
android:settingsActivity=".ServiceSettingsActivity" />
Related
I have enabled accessibility-service permission and now i want to get url from address bar.
I have tried below thing :
accessibility_service_config.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="#string/accessibility_service_description"
android:notificationTimeout="0"
android:canRequestFilterKeyEvents="true"
android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity" />
AccessService.java
public class AccessService extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null)
return;
final String packageName = String.valueOf(source.getPackageName());
String BROWSER_LIST = "com.android.chrome";
List<String> browserList
= Arrays.asList(BROWSER_LIST.split(",\\s*"));
if (event.getEventType()
== AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
if (!browserList.contains(packageName)) {
return;
}
}
if (browserList.contains(packageName)) {
try {
if (AccessibilityEvent
.eventTypeToString(event.getEventType())
.contains("WINDOW")) {
AccessibilityNodeInfo nodeInfo = event.getSource();
getUrlsFromViews(nodeInfo);
}
} catch (StackOverflowError ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public void getUrlsFromViews(AccessibilityNodeInfo info) {
try {
if (info == null)
return;
if (info.getText() != null && info.getText().length() > 0) {
String capturedText = info.getText().toString();
Bundle arguments = new Bundle();
if (capturedText.contains("https://")
|| capturedText.contains("http://")) {
if (capturedText.contains("facebook.com")) {
// open new tab
}
}
}
for (int i = 0; i < info.getChildCount(); i++) {
AccessibilityNodeInfo child = info.getChild(i);
getUrlsFromViews(child);
if (child != null) {
child.recycle();
}
}
} catch (StackOverflowError ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void onInterrupt() {
}
#Override
protected void onServiceConnected() {
super.onServiceConnected();
}
}
The problem here i am facing here is when i type facebook.com in address bar and hit the url then i am getting only facebook.com or m.facebook.com and for this reason i am not able to take any action.
I want to get URL only after it is hit in address bar. Also i want to open new tab and close existing tab when it will facebook.com in address bar.
Is there any proper way to do this ?
I have something to add here and how we can improve your solution. It could be better if you accept the following limitations:
The application has an embedded list of supported browsers. Any other browser won't be supported.
The accessibility service will look for an id of an address bar text field and try to intercept the URL from there. It is not possible to catch directly the URL which is going to be loaded. To find this id we should perform a bit of reverse engineering of the target browser: collect all fields by accessibility service and compare their ids and values with user input.
From the previous point, the next limitation is that we are going to support only the current version of the browser. The id could be changed in the future by 3rd-party browser developers and we will have to update our interceptor to continue support. This could be done either by updating an app or by providing the browser package to id mapping by the remote server
We detect either manual user input or redirect on link pressing (because in this case, the new URL will be also visible in an address bar). BTW it is not clear what you have mean when said
I want to get the URL only after it is hit in the address bar.
The last limitation. Despite we are able to intercept the URL and redirect user to another page we can't prevent the site from being loaded or being started to load due to delays of asynchronous parsing a browser app screen. E.g. it is not really safe to protect a user from any access to a fraud site
The implementation:
public class UrlInterceptorService extends AccessibilityService {
private HashMap<String, Long> previousUrlDetections = new HashMap<>();
#Override
protected void onServiceConnected() {
AccessibilityServiceInfo info = getServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
info.packageNames = packageNames();
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_VISUAL;
//throttling of accessibility event notification
info.notificationTimeout = 300;
//support ids interception
info.flags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS |
AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
this.setServiceInfo(info);
}
private String captureUrl(AccessibilityNodeInfo info, SupportedBrowserConfig config) {
List<AccessibilityNodeInfo> nodes = info.findAccessibilityNodeInfosByViewId(config.addressBarId);
if (nodes == null || nodes.size() <= 0) {
return null;
}
AccessibilityNodeInfo addressBarNodeInfo = nodes.get(0);
String url = null;
if (addressBarNodeInfo.getText() != null) {
url = addressBarNodeInfo.getText().toString();
}
addressBarNodeInfo.recycle();
return url;
}
#Override
public void onAccessibilityEvent(#NonNull AccessibilityEvent event) {
AccessibilityNodeInfo parentNodeInfo = event.getSource();
if (parentNodeInfo == null) {
return;
}
String packageName = event.getPackageName().toString();
SupportedBrowserConfig browserConfig = null;
for (SupportedBrowserConfig supportedConfig: getSupportedBrowsers()) {
if (supportedConfig.packageName.equals(packageName)) {
browserConfig = supportedConfig;
}
}
//this is not supported browser, so exit
if (browserConfig == null) {
return;
}
String capturedUrl = captureUrl(parentNodeInfo, browserConfig);
parentNodeInfo.recycle();
//we can't find a url. Browser either was updated or opened page without url text field
if (capturedUrl == null) {
return;
}
long eventTime = event.getEventTime();
String detectionId = packageName + ", and url " + capturedUrl;
//noinspection ConstantConditions
long lastRecordedTime = previousUrlDetections.containsKey(detectionId) ? previousUrlDetections.get(detectionId) : 0;
//some kind of redirect throttling
if (eventTime - lastRecordedTime > 2000) {
previousUrlDetections.put(detectionId, eventTime);
analyzeCapturedUrl(capturedUrl, browserConfig.packageName);
}
}
private void analyzeCapturedUrl(#NonNull String capturedUrl, #NonNull String browserPackage) {
String redirectUrl = "your redirect url is here";
if (capturedUrl.contains("facebook.com")) {
performRedirect(redirectUrl, browserPackage);
}
}
/** we just reopen the browser app with our redirect url using service context
* We may use more complicated solution with invisible activity to send a simple intent to open the url */
private void performRedirect(#NonNull String redirectUrl, #NonNull String browserPackage) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl));
intent.setPackage(browserPackage);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackage);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
catch(ActivityNotFoundException e) {
// the expected browser is not installed
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl));
startActivity(i);
}
}
#Override
public void onInterrupt() { }
#NonNull
private static String[] packageNames() {
List<String> packageNames = new ArrayList<>();
for (SupportedBrowserConfig config: getSupportedBrowsers()) {
packageNames.add(config.packageName);
}
return packageNames.toArray(new String[0]);
}
private static class SupportedBrowserConfig {
public String packageName, addressBarId;
public SupportedBrowserConfig(String packageName, String addressBarId) {
this.packageName = packageName;
this.addressBarId = addressBarId;
}
}
/** #return a list of supported browser configs
* This list could be instead obtained from remote server to support future browser updates without updating an app */
#NonNull
private static List<SupportedBrowserConfig> getSupportedBrowsers() {
List<SupportedBrowserConfig> browsers = new ArrayList<>();
browsers.add( new SupportedBrowserConfig("com.android.chrome", "com.android.chrome:id/url_bar"));
browsers.add( new SupportedBrowserConfig("org.mozilla.firefox", "org.mozilla.firefox:id/url_bar_title"));
return browsers;
}
}
and accessibility service config:
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/accessibility_service_description"
android:canRetrieveWindowContent="true"
android:settingsActivity=".ServiceSettingsActivity" />
Feel free to ask any questions and I'll try to help
first, add flag and package in accessibility_service XML
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews|flagRequestTouchExplorationMode|flagRequestEnhancedWebAccessibility|flagReportViewIds|flagRetrieveInteractiveWindows"
android:canRetrieveWindowContent="true"
android:description="#string/accessibility_service_description"
android:notificationTimeout="0"
android:canRequestFilterKeyEvents="true"
android:packageNames="com.android.chrome"
android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity" />
in AndroidManifest:
<service android:name=".MyAccessibilityService"
android:label="#string/accessibility_title"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/accessibility_service" />
</service>
class java:
public class ASUrl extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if(AccessibilityEvent.eventTypeToString(event.getEventType()).contains("WINDOW")){
AccessibilityNodeInfo nodeInfo = event.getSource();
dfs(nodeInfo);
}
}
/**
* Method to loop through all the views and try to find a URL.
* #param info
*/
public void getUrlsFromViews(AccessibilityNodeInfo info) {
if(info == null)
return;
if(info.getText() != null && info.getText().length() > 0)
System.out.println(info.getText() + " class: "+info.getClassName());
for(int i=0;i<info.getChildCount();i++){
AccessibilityNodeInfo child = info.getChild(i);
getUrlsFromViews(child);
if(child != null){
child.recycle();
}
}
}
}
I am using the following code to display gif image, png image when clicking on buttons in the custom keyboard.when clicking on the button the images are sent in the hangouts but it is unable to send in messenger. The buttons are not in enable state.
what I have to do set buttons to enable state when this custom keyboard visible in messenger.Please help me to resolve this issue.
Here is the My code.
public class ImageKeyboard extends InputMethodService {
private static final String TAG = "ImageKeyboard";
private static final String AUTHORITY = "com.example.android.commitcontent.ime.inputcontent";
private static final String MIME_TYPE_GIF = "image/gif";
private static final String MIME_TYPE_PNG = "image/png";
private static final String MIME_TYPE_WEBP = "image/webp";
private File mPngFile;
private File mGifFile;
private File mWebpFile;
private Button mGifButton;
private Button mPngButton;
private Button mWebpButton;
private boolean isCommitContentSupported(
#Nullable EditorInfo editorInfo, #NonNull String mimeType) {
if (editorInfo == null) {
return false;
}
final InputConnection ic = getCurrentInputConnection();
if (ic == null) {
return false;
}
if (!validatePackageName(editorInfo)) {
return false;
}
final String[] supportedMimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);
for (String supportedMimeType : supportedMimeTypes) {
if (ClipDescription.compareMimeTypes(mimeType, supportedMimeType)) {
return true;
}
}
return false;
}
private void doCommitContent(#NonNull String description, #NonNull String mimeType,
#NonNull File file) {
final EditorInfo editorInfo = getCurrentInputEditorInfo();
// Validate packageName again just in case.
if (!validatePackageName(editorInfo)) {
return;
}
final Uri contentUri = FileProvider.getUriForFile(this, AUTHORITY, file);
// As you as an IME author are most likely to have to implement your own content provider
// to support CommitContent API, it is important to have a clear spec about what
// applications are going to be allowed to access the content that your are going to share.
final int flag;
if (Build.VERSION.SDK_INT >= 25) {
// On API 25 and later devices, as an analogy of Intent.FLAG_GRANT_READ_URI_PERMISSION,
// you can specify InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION to give
// a temporary read access to the recipient application without exporting your content
// provider.
flag = InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
} else {
// On API 24 and prior devices, we cannot rely on
// InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION. You as an IME author
// need to decide what access control is needed (or not needed) for content URIs that
// you are going to expose. This sample uses Context.grantUriPermission(), but you can
// implement your own mechanism that satisfies your own requirements.
flag = 0;
try {
// TODO: Use revokeUriPermission to revoke as needed.
grantUriPermission(
editorInfo.packageName, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
} catch (Exception e){
Log.e(TAG, "grantUriPermission failed packageName=" + editorInfo.packageName
+ " contentUri=" + contentUri, e);
}
}
final InputContentInfoCompat inputContentInfoCompat = new InputContentInfoCompat(
contentUri,
new ClipDescription(description, new String[]{mimeType}),
null /* linkUrl */);
InputConnectionCompat.commitContent(
getCurrentInputConnection(), getCurrentInputEditorInfo(), inputContentInfoCompat,
flag, null);
}
private boolean validatePackageName(#Nullable EditorInfo editorInfo) {
if (editorInfo == null) {
return false;
}
final String packageName = editorInfo.packageName;
if (packageName == null) {
return false;
}
// In Android L MR-1 and prior devices, EditorInfo.packageName is not a reliable identifier
// of the target application because:
// 1. the system does not verify it [1]
// 2. InputMethodManager.startInputInner() had filled EditorInfo.packageName with
// view.getContext().getPackageName() [2]
// [1]: https://android.googlesource.com/platform/frameworks/base/+/a0f3ad1b5aabe04d9eb1df8bad34124b826ab641
// [2]: https://android.googlesource.com/platform/frameworks/base/+/02df328f0cd12f2af87ca96ecf5819c8a3470dc8
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return true;
}
final InputBinding inputBinding = getCurrentInputBinding();
if (inputBinding == null) {
// Due to b.android.com/225029, it is possible that getCurrentInputBinding() returns
// null even after onStartInputView() is called.
// TODO: Come up with a way to work around this bug....
Log.e(TAG, "inputBinding should not be null here. "
+ "You are likely to be hitting b.android.com/225029");
return false;
}
final int packageUid = inputBinding.getUid();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final AppOpsManager appOpsManager =
(AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
try {
appOpsManager.checkPackage(packageUid, packageName);
} catch (Exception e) {
return false;
}
return true;
}
final PackageManager packageManager = getPackageManager();
final String possiblePackageNames[] = packageManager.getPackagesForUid(packageUid);
for (final String possiblePackageName : possiblePackageNames) {
if (packageName.equals(possiblePackageName)) {
return true;
}
}
return false;
}
#Override
public void onCreate() {
super.onCreate();
// TODO: Avoid file I/O in the main thread.
final File imagesDir = new File(getFilesDir(), "images");
imagesDir.mkdirs();
mGifFile = getFileForResource(this, R.raw.animated_gif, imagesDir, "image.gif");
mPngFile = getFileForResource(this, R.raw.dessert_android, imagesDir, "image.png");
mWebpFile = getFileForResource(this, R.raw.animated_webp, imagesDir, "image.webp");
}
#Override
public View onCreateInputView() {
mGifButton = new Button(this);
mGifButton.setText("Insert GIF");
mGifButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ImageKeyboard.this.doCommitContent("A waving flag", MIME_TYPE_GIF, mGifFile);
}
});
mPngButton = new Button(this);
mPngButton.setText("Insert PNG");
mPngButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ImageKeyboard.this.doCommitContent("A droid logo", MIME_TYPE_PNG, mPngFile);
}
});
mWebpButton = new Button(this);
mWebpButton.setText("Insert WebP");
mWebpButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ImageKeyboard.this.doCommitContent(
"Android N recovery animation", MIME_TYPE_WEBP, mWebpFile);
}
});
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.addView(mGifButton);
layout.addView(mPngButton);
layout.addView(mWebpButton);
return layout;
}
#Override
public boolean onEvaluateFullscreenMode() {
// In full-screen mode the inserted content is likely to be hidden by the IME. Hence in this
// sample we simply disable full-screen mode.
return false;
}
#Override
public void onStartInputView(EditorInfo info, boolean restarting) {
mGifButton.setEnabled(mGifFile != null && isCommitContentSupported(info, MIME_TYPE_GIF));
mPngButton.setEnabled(mPngFile != null && isCommitContentSupported(info, MIME_TYPE_PNG));
mWebpButton.setEnabled(mWebpFile != null && isCommitContentSupported(info, MIME_TYPE_WEBP));
}
private static File getFileForResource(
#NonNull Context context, #RawRes int res, #NonNull File outputDir,
#NonNull String filename) {
final File outputFile = new File(outputDir, filename);
final byte[] buffer = new byte[4096];
InputStream resourceReader = null;
try {
try {
resourceReader = context.getResources().openRawResource(res);
OutputStream dataWriter = null;
try {
dataWriter = new FileOutputStream(outputFile);
while (true) {
final int numRead = resourceReader.read(buffer);
if (numRead <= 0) {
break;
}
dataWriter.write(buffer, 0, numRead);
}
return outputFile;
} finally {
if (dataWriter != null) {
dataWriter.flush();
dataWriter.close();
}
}
} finally {
if (resourceReader != null) {
resourceReader.close();
}
}
} catch (IOException e) {
return null;
}
}
}
I am developing an app which contain broadcast receiver and also contain list of app when user click any particular app he will directed to play store and download that app from jsons response I get all app showing in al list url like "https://play.google.com/store/apps/details?id=com.example.launcher" what I have to do is that when app is successfully downloaded in system I will check that app package name of that app from json response url if match then show a toast else error message.
How can I acheive this pls help me Its for a week I am searching.
here is my code for broadcast receiver:-
public class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.PACKAGE_ADDED"))
Log.e("Package Added:-", intent.getData().toString());
}
}
#Override
public void onReceive(Context context, Intent intent) {
String installAppPackgeName = intent.getData().getSchemeSpecificPart();
String url = "https://play.google.com/store/apps/details?id=com.example.launcher";
String packageName = getQueryMap(url).get("id");
if(installAppPackgeName.equals(packageName)){
// If install app package name is same as url package name then do some task.
// Your app is installed now.
}
}
/**
* Get Query String as Key-Value Pair.
*/
public static Map<String, String> getQueryMap(String query) {
Map<String, String> map = new HashMap<>();
String[] queryString = query.split("\\?");
if(queryString.length>1){
String[] params = queryString[1].split("&");
for (String param : params) {
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
}
}
return map;
}
extract package name from URL with
String downPack = yourUrl.split("id\\=")[1];
extract package name from file
public String getPackageNameByAPK(String apkPath, Context context) {
String packName = "";
PackageManager packageManager;
PackageInfo packInfo;
try {
if (apkPath == null) return "";
if (context == null) {
return "";
}
packageManager = context.getPackageManager();
packInfo = packageManager.getPackageArchiveInfo(apkPath, 0);
packName = packInfo.packageName;
} catch (Exception e) {
e.printStackTrace();
}
return packName;
}
compare like
boolean matched = downPack.equals(getPackageNameByAPK(apkPath,context));
This may help you..
I'm trying to build an android youtube app and to start it off, I would like the main page to display popular videos that are featured on youtube's homepage.
How can I get the necessary info to implement it on my listview? I was digging around the google developers website but could not find any lead as to how to request such information.
any help would be appreciated!
thank you!
Use the chart filter as mentioned here with the value mostPopular. See the sample code on that link for more details.
I have fetched the top trending videos and play them on the app iteself .
First you need to sign in via google , this can be done in simple code structure given below.
private static GoogleAccountCredential credential;
public static final String[] SCOPES = {Scopes.PROFILE,
YouTubeScopes.YOUTUBE};
private static final String PREF_ACCOUNT_NAME = "";
private HttpTransport transport = AndroidHttp.newCompatibleTransport();
private JsonFactory jsonFactory = new GsonFactory();
public static final String APP_NAME = "Your Google Project Name";
credential = GoogleAccountCredential.usingOAuth2(
getActivity().getApplicationContext(),
Arrays.asList(Auth.SCOPES));
credential.setBackOff(new ExponentialBackOff());
scopes =
Lists.newArrayList("https://www.googleapis.com/auth/youtube");
credential.setSelectedAccountName(PREF_ACCOUNT_NAME);
After selecting account , you can load videos into your app - Call loadVideos().
public void loadVideos()
{
if (PREF_ACCOUNT_NAME == null)
{
System.out.println("First need to authorize app");
return;
}
new AsyncTask<Void, Void, List<VideoData>>()
{
#Override
protected List<VideoData> doInBackground (Void...voids){
credential.setSelectedAccountName(name);
YouTube youtube = new YouTube.Builder(transport,
jsonFactory,
credential).setApplicationName(APP_NAME)
.build();
try {
videos = new ArrayList<VideoData>();
VideoListResponse videoListResponse = youtube.videos().
list("snippet,statistics")
.setChart("mostPopular")
.setMaxResults((long)20)
.setRegionCode("IN").
execute();
List<Video> videoList = videoListResponse.getItems();
if (videoList.isEmpty()) {
System.out.println("Can't find a video with ID: ");
return null;
}
for (Video video : videoListResponse.getItems()) {
VideoData videoData = new VideoData();
videoData.setVideo(video);
videos.add(videoData);
}
System.out.println(videoList.size());
return videos;
} catch (final GooglePlayServicesAvailabilityIOException
availabilityException) {
} catch (UserRecoverableAuthIOException
userRecoverableException) {
startActivityForResult(
userRecoverableException.getIntent(),
REQUEST_AUTHORIZATION);
} catch (IOException e) {
Utils.logAndShow(getActivity(), Constants.APP_NAME, e);
}
return null;
}
#Override
protected void onPostExecute (List < VideoData > videos) {
pb.setVisibility(View.GONE);
if (videos == null) {
return;
}
}
}
.execute((Void)
null);
}
VideoClass contains- getter setter for getting data.
public class VideoData {
private Video mVideo;
public Video getVideo() {
return mVideo;
}
public void setVideo(Video video) {
mVideo = video;
}
public String getYouTubeId() {
return mVideo.getId();
}
public String getTitle() {
return mVideo.getSnippet().getTitle();
}
public String getLikes(){
return String.valueOf(mVideo.getStatistics().getLikeCount());
}
public String getViewCount(){
return mVideo.getStatistics().getViewCount().toString();
}
public VideoSnippet addTags(Collection<? extends String> tags) {
VideoSnippet mSnippet = mVideo.getSnippet();
List<String> mTags = mSnippet.getTags();
if (mTags == null) {
mTags = new ArrayList<String>(2);
}
mTags.addAll(tags);
return mSnippet;
}
public String getThumbUri() {
return mVideo.getSnippet().getThumbnails().getHigh().getUrl();
}
public String getWatchUri() {
return "http://www.youtube.com/watch?v=" + getYouTubeId();
}
}
You can manipulate VideoList by applying filter to Videoresponse list.
Filters can be find at Youtube Api v3
For your information below Code Refer to link https://www.googleapis.com/youtube/v3
. App package iteself forms the link.
new YouTube.Builder(transport,
jsonFactory,
credential).setApplicationName(APP_NAME)
.build();
Let me know if you have doubts or need working prototype.
On the iPhone, you can add a numbered badge to the application icon. On BlackBerry, I've successfully painted an image onto the application's icon while in the program. I want to do this for Android as well. I don't want to use the notification bar, as it's not something that needs to be notified instantly. Instead, I just want the user to be able to see how many new messages are in the application just by looking at the application icon.
Unfortunately, Android does not allow changing of the application icon because it's sealed in the APK once the program is compiled. There is no way to programmatically change it to a 'drawable'.
You may achieve your goal by using a widget instead of an icon. Widgets are highly customisable and can do what you want.
There's a short discussion about the difference between iPhone icon notification and using widgets here:
http://www.cnet.com/8301-19736_1-10278814-251.html
As you'll notice, there is virtually no difference between using a widget or an icon, since they can be the same size and look the same.
This can also be done for Sony's Xperia Home. I've blogged about it here, but the important parts are below. Sony devices use a class named BadgeReciever.
Declare the com.sonyericsson.home.permission.BROADCAST_BADGE permission in your manifest file:
Broadcast an Intent to the BadgeReceiver:
Intent intent = new Intent();
intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", "com.yourdomain.yourapp.MainActivity");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", true);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", "99");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", "com.yourdomain.yourapp");
sendBroadcast(intent);
Done. Once this Intent is broadcast the launcher should show a badge on your application icon.
To remove the badge again, simply send a new broadcast, this time with SHOW_MESSAGE set to false:
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", false);
I've excluded details on how I found this to keep the answer short, but it's all available in the blog. Might be an interesting read for someone.
I've also posted a seperate SO question about this here and will add the full answer there once I'm allowed to (need 10 reputation to answer my own question within 8 hours).
ShortcutBadger library makes it possible and works with LG, Sony, Samsung, HTC and other custom Launchers.
It even has a way to display Badge Count in Pure Android devices desktop.
Updating the Badge Count in the application icon is as easy as calling:
int badgeCount = 1;
ShortcutBadger.setBadge(getApplicationContext(), badgeCount);
It includes a demo application that allows you to test its behaviour.
OR
you can also try activity-alias to do so, but in this you need to create different icons with badge values ,it will work great in case- you need to switch between 2 different App icons (need to create different activity-alias for displaying different icon i.e more icons = more activity-alias).
Asus devices:
public static class AsusHomeBadger implements Badger {
private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";
#Override
public void executeBadge(int badgeCount) {
final Intent intent = new Intent(INTENT_ACTION);
intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
intent.putExtra("badge_vip_count", 0);
if (canResolveBroadcast(intent)) {
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
ApplicationLoader.applicationContext.sendBroadcast(intent);
}
});
}
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList("com.asus.launcher");
}
}
Huawei devices:
public static class HuaweiHomeBadger implements Badger {
#Override
public void executeBadge(int badgeCount) {
final Bundle localBundle = new Bundle();
localBundle.putString("package", ApplicationLoader.applicationContext.getPackageName());
localBundle.putString("class", componentName.getClassName());
localBundle.putInt("badgenumber", badgeCount);
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
try {
ApplicationLoader.applicationContext.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, localBundle);
} catch (Exception e) {
FileLog.e(e);
}
}
});
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList(
"com.huawei.android.launcher"
);
}
}
HTC devices:
public static class NewHtcHomeBadger implements Badger {
public static final String INTENT_UPDATE_SHORTCUT = "com.htc.launcher.action.UPDATE_SHORTCUT";
public static final String INTENT_SET_NOTIFICATION = "com.htc.launcher.action.SET_NOTIFICATION";
public static final String PACKAGENAME = "packagename";
public static final String COUNT = "count";
public static final String EXTRA_COMPONENT = "com.htc.launcher.extra.COMPONENT";
public static final String EXTRA_COUNT = "com.htc.launcher.extra.COUNT";
#Override
public void executeBadge(int badgeCount) {
final Intent intent1 = new Intent(INTENT_SET_NOTIFICATION);
intent1.putExtra(EXTRA_COMPONENT, componentName.flattenToShortString());
intent1.putExtra(EXTRA_COUNT, badgeCount);
final Intent intent = new Intent(INTENT_UPDATE_SHORTCUT);
intent.putExtra(PACKAGENAME, componentName.getPackageName());
intent.putExtra(COUNT, badgeCount);
if (canResolveBroadcast(intent1) || canResolveBroadcast(intent)) {
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
ApplicationLoader.applicationContext.sendBroadcast(intent1);
ApplicationLoader.applicationContext.sendBroadcast(intent);
}
});
}
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList("com.htc.launcher");
}
}
Samsung devices:
public static class SamsungHomeBadger implements Badger {
private static final String CONTENT_URI = "content://com.sec.badge/apps?notify=true";
private static final String[] CONTENT_PROJECTION = new String[]{"_id","class"};
private static DefaultBadger defaultBadger;
#Override
public void executeBadge(int badgeCount) {
try {
if (defaultBadger == null) {
defaultBadger = new DefaultBadger();
}
defaultBadger.executeBadge(badgeCount);
} catch (Exception ignore) {
}
Uri mUri = Uri.parse(CONTENT_URI);
ContentResolver contentResolver = ApplicationLoader.applicationContext.getContentResolver();
Cursor cursor = null;
try {
cursor = contentResolver.query(mUri, CONTENT_PROJECTION, "package=?", new String[]{componentName.getPackageName()}, null);
if (cursor != null) {
String entryActivityName = componentName.getClassName();
boolean entryActivityExist = false;
while (cursor.moveToNext()) {
int id = cursor.getInt(0);
ContentValues contentValues = getContentValues(componentName, badgeCount, false);
contentResolver.update(mUri, contentValues, "_id=?", new String[]{String.valueOf(id)});
if (entryActivityName.equals(cursor.getString(cursor.getColumnIndex("class")))) {
entryActivityExist = true;
}
}
if (!entryActivityExist) {
ContentValues contentValues = getContentValues(componentName, badgeCount, true);
contentResolver.insert(mUri, contentValues);
}
}
} finally {
close(cursor);
}
}
private ContentValues getContentValues(ComponentName componentName, int badgeCount, boolean isInsert) {
ContentValues contentValues = new ContentValues();
if (isInsert) {
contentValues.put("package", componentName.getPackageName());
contentValues.put("class", componentName.getClassName());
}
contentValues.put("badgecount", badgeCount);
return contentValues;
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList(
"com.sec.android.app.launcher",
"com.sec.android.app.twlauncher"
);
}
}
Sony devices:
public static class SonyHomeBadger implements Badger {
private static final String INTENT_ACTION = "com.sonyericsson.home.action.UPDATE_BADGE";
private static final String INTENT_EXTRA_PACKAGE_NAME = "com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME";
private static final String INTENT_EXTRA_ACTIVITY_NAME = "com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME";
private static final String INTENT_EXTRA_MESSAGE = "com.sonyericsson.home.intent.extra.badge.MESSAGE";
private static final String INTENT_EXTRA_SHOW_MESSAGE = "com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE";
private static final String PROVIDER_CONTENT_URI = "content://com.sonymobile.home.resourceprovider/badge";
private static final String PROVIDER_COLUMNS_BADGE_COUNT = "badge_count";
private static final String PROVIDER_COLUMNS_PACKAGE_NAME = "package_name";
private static final String PROVIDER_COLUMNS_ACTIVITY_NAME = "activity_name";
private static final String SONY_HOME_PROVIDER_NAME = "com.sonymobile.home.resourceprovider";
private final Uri BADGE_CONTENT_URI = Uri.parse(PROVIDER_CONTENT_URI);
private static AsyncQueryHandler mQueryHandler;
#Override
public void executeBadge(int badgeCount) {
if (sonyBadgeContentProviderExists()) {
executeBadgeByContentProvider(badgeCount);
} else {
executeBadgeByBroadcast(badgeCount);
}
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList("com.sonyericsson.home", "com.sonymobile.home");
}
private static void executeBadgeByBroadcast(int badgeCount) {
final Intent intent = new Intent(INTENT_ACTION);
intent.putExtra(INTENT_EXTRA_PACKAGE_NAME, componentName.getPackageName());
intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
intent.putExtra(INTENT_EXTRA_MESSAGE, String.valueOf(badgeCount));
intent.putExtra(INTENT_EXTRA_SHOW_MESSAGE, badgeCount > 0);
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
ApplicationLoader.applicationContext.sendBroadcast(intent);
}
});
}
private void executeBadgeByContentProvider(int badgeCount) {
if (badgeCount < 0) {
return;
}
if (mQueryHandler == null) {
mQueryHandler = new AsyncQueryHandler(ApplicationLoader.applicationContext.getApplicationContext().getContentResolver()) {
#Override
public void handleMessage(Message msg) {
try {
super.handleMessage(msg);
} catch (Throwable ignore) {
}
}
};
}
insertBadgeAsync(badgeCount, componentName.getPackageName(), componentName.getClassName());
}
private void insertBadgeAsync(int badgeCount, String packageName, String activityName) {
final ContentValues contentValues = new ContentValues();
contentValues.put(PROVIDER_COLUMNS_BADGE_COUNT, badgeCount);
contentValues.put(PROVIDER_COLUMNS_PACKAGE_NAME, packageName);
contentValues.put(PROVIDER_COLUMNS_ACTIVITY_NAME, activityName);
mQueryHandler.startInsert(0, null, BADGE_CONTENT_URI, contentValues);
}
private static boolean sonyBadgeContentProviderExists() {
boolean exists = false;
ProviderInfo info = ApplicationLoader.applicationContext.getPackageManager().resolveContentProvider(SONY_HOME_PROVIDER_NAME, 0);
if (info != null) {
exists = true;
}
return exists;
}
}
Xiaomi devices:
public static class XiaomiHomeBadger implements Badger {
public static final String INTENT_ACTION = "android.intent.action.APPLICATION_MESSAGE_UPDATE";
public static final String EXTRA_UPDATE_APP_COMPONENT_NAME = "android.intent.extra.update_application_component_name";
public static final String EXTRA_UPDATE_APP_MSG_TEXT = "android.intent.extra.update_application_message_text";
#Override
public void executeBadge(int badgeCount) {
try {
Class miuiNotificationClass = Class.forName("android.app.MiuiNotification");
Object miuiNotification = miuiNotificationClass.newInstance();
Field field = miuiNotification.getClass().getDeclaredField("messageCount");
field.setAccessible(true);
field.set(miuiNotification, String.valueOf(badgeCount == 0 ? "" : badgeCount));
} catch (Throwable e) {
final Intent localIntent = new Intent(INTENT_ACTION);
localIntent.putExtra(EXTRA_UPDATE_APP_COMPONENT_NAME, componentName.getPackageName() + "/" + componentName.getClassName());
localIntent.putExtra(EXTRA_UPDATE_APP_MSG_TEXT, String.valueOf(badgeCount == 0 ? "" : badgeCount));
if (canResolveBroadcast(localIntent)) {
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
ApplicationLoader.applicationContext.sendBroadcast(localIntent);
}
});
}
}
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList(
"com.miui.miuilite",
"com.miui.home",
"com.miui.miuihome",
"com.miui.miuihome2",
"com.miui.mihome",
"com.miui.mihome2"
);
}
}
As of API 26, this is now officially supported:
Starting with 8.0 (API level 26), notification badges (also known as notification dots) appear on a launcher icon when the associated app has an active notification. Users can long-press on the app icon to reveal the notifications (alongside any app shortcuts), as shown in figure 1.
These dots appear by default in launcher apps that support them and there's nothing your app needs to do. However, there might be situations in which you don't want the to notification dot to appear or you want to control exactly which notifications to appear there.
To set a custom number, call setNumber() on the notification:
mNotification.setNumber(messageCount)
Here's how to do it for:
Apex Launcher
Nova Launcer
I think there's also a way to do it on the LG launcher, but haven't found out how yet.