2014-11-05 20 views
5

के साथ विफल पाश मैं अपनी परियोजना में com.google.android.gms.common.api.GoogleApiClient को लागू करने का प्रयास कर रहा हूं।कनेक्शन में फंस गया GoogleApiClient

समस्या यह है कि हर बार जब मैं कनेक्ट करने का प्रयास करता हूं, तो मुझे निष्पादित करने के लंबित इरादे के साथ ऑनकनेक्शनफेल श्रोता पर वापस कॉल मिलता है। एक साफ इंस्टॉल पर, पहला लंबित इरादा खाता चयन स्क्रीन लॉन्च करेगा। यह उम्मीद है। ऐप के बाद के प्रत्येक पुनरारंभ खाते के चयन को बाईपास कर देगा, जब तक कि एप्लिकेशन मैनेजर में ऐप का डेटा साफ़ नहीं किया जाता है।

खाता-चयन स्क्रीन के बाद, साइन-इन स्क्रीन ब्रेफली दिखाई देगी। हालांकि यह कभी संकेत नहीं करता है। साइन-इन स्क्रीन चमक के बाद ऑनएक्टिविटी रिसेट को कॉल किया जाएगा, जो क्लाइंट को कनेक्ट करने का प्रयास करता है। यह कनेक्ट नहीं होता है, और ऑनकनेक्शनफेल श्रोता को फिर से कॉल करता है।

यदि मैं इरादों को निष्पादित करने की कोशिश करता रहता हूं, तो मैं साइन इन स्क्रीन पर हस्ताक्षर करने के साथ लूप में फंस जाता हूं, फिर गायब हो जाता हूं, लेकिन कभी कनेक्ट नहीं होता या साइन इन नहीं करता हूं। ConnectionResult.toString "Sign_In_Required" इंगित करता है, और एक त्रुटि देता है 4 का कोड (जैसा कि Sign_In_Required स्थिर है।

एपीआई कंसोल पर, मैंने एक ओथ 2.0 क्लाइंट आईडी और एंड्रॉइड एप्लिकेशन के लिए एक सार्वजनिक एपीआई एक्सेस कुंजी लागू की है। विशेष रूप से, मेरा ऐप पुराने कॉम का उपयोग करके काम करता है। google.api.services.drive.Drive ग्राहक

मेरी कोड के लिए के रूप में:।

मैंने दो अलग-अलग कार्यान्वयन here और here का उपयोग करने का प्रयास किया है। मैंने दूसरे उदाहरण को लागू करने की कोशिश की जितनी संभव हो सके बदलाव करें। यह नीचे गयी है:

public class MainActivity extends Activity implements ConnectionCallbacks, 
    OnConnectionFailedListener { 

private static final String TAG = "android-drive-quickstart"; 
private static final int REQUEST_CODE_CAPTURE_IMAGE = 1; 
private static final int REQUEST_CODE_CREATOR = 2; 
private static final int REQUEST_CODE_RESOLUTION = 3; 

private GoogleApiClient mGoogleApiClient; 
private Bitmap mBitmapToSave; 

/** 
* Create a new file and save it to Drive. 
*/ 
private void saveFileToDrive() { 
    // Start by creating a new contents, and setting a callback. 
    Log.i(TAG, "Creating new contents."); 
    final Bitmap image = mBitmapToSave; 

    Drive.DriveApi.newContents(mGoogleApiClient).setResultCallback(new ResultCallback<DriveApi.ContentsResult>() { 

     @Override 
     public void onResult(DriveApi.ContentsResult result) { 

      // If the operation was not successful, we cannot do anything 
      // and must 
      // fail. 
      if (!result.getStatus().isSuccess()) { 
       Log.i(TAG, "Failed to create new contents."); 
       return; 
      } 
      // Otherwise, we can write our data to the new contents. 
      Log.i(TAG, "New contents created."); 
      // Get an output stream for the contents. 
      OutputStream outputStream = result.getContents().getOutputStream(); 
      // Write the bitmap data from it. 
      ByteArrayOutputStream bitmapStream = new ByteArrayOutputStream(); 
      image.compress(Bitmap.CompressFormat.PNG, 100, bitmapStream); 
      try { 
       outputStream.write(bitmapStream.toByteArray()); 
      } catch (IOException e1) { 
       Log.i(TAG, "Unable to write file contents."); 
      } 
      // Create the initial metadata - MIME type and title. 
      // Note that the user will be able to change the title later. 
      MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder() 
        .setMimeType("image/jpeg").setTitle("Android Photo.png").build(); 
      // Create an intent for the file chooser, and start it. 
      IntentSender intentSender = Drive.DriveApi 
        .newCreateFileActivityBuilder() 
        .setInitialMetadata(metadataChangeSet) 
        .setInitialContents(result.getContents()) 
        .build(mGoogleApiClient); 
      try { 
       startIntentSenderForResult(
         intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0); 
      } catch (SendIntentException e) { 
       Log.i(TAG, "Failed to launch file chooser."); 
      } 
     } 
    }); 
} 

@Override 
protected void onResume() { 
    super.onResume(); 
    if (mGoogleApiClient == null) { 
     // Create the API client and bind it to an instance variable. 
     // We use this instance as the callback for connection and connection 
     // failures. 
     // Since no account name is passed, the user is prompted to choose. 
     mGoogleApiClient = new GoogleApiClient.Builder(this) 
       .addApi(Drive.API) 
       .addScope(Drive.SCOPE_FILE) 
       .addConnectionCallbacks(this) 
       .addOnConnectionFailedListener(this) 
       .build(); 
    } 
    // Connect the client. Once connected, the camera is launched. 
    mGoogleApiClient.connect(); 
} 

@Override 
protected void onPause() { 
    if (mGoogleApiClient != null) { 
     mGoogleApiClient.disconnect(); 
    } 
    super.onPause(); 
} 

@Override 
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { 
    switch (requestCode) { 
     case REQUEST_CODE_CAPTURE_IMAGE: 
      // Called after a photo has been taken. 
      if (resultCode == Activity.RESULT_OK) { 
       // Store the image data as a bitmap for writing later. 
       mBitmapToSave = (Bitmap) data.getExtras().get("data"); 
      } 
      break; 
     case REQUEST_CODE_CREATOR: 
      // Called after a file is saved to Drive. 
      if (resultCode == RESULT_OK) { 
       Log.i(TAG, "Image successfully saved."); 
       mBitmapToSave = null; 
       // Just start the camera again for another photo. 
       startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE), 
         REQUEST_CODE_CAPTURE_IMAGE); 
      } 
      break; 
    } 
} 

@Override 
public void onConnectionFailed(ConnectionResult result) { 
    // Called whenever the API client fails to connect. 
    Log.i(TAG, "GoogleApiClient connection failed: " + result.toString()); 
    if (!result.hasResolution()) { 
     // show the localized error dialog. 
     GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show(); 
     return; 
    } 
    // The failure has a resolution. Resolve it. 
    // Called typically when the app is not yet authorized, and an 
    // authorization 
    // dialog is displayed to the user. 
    try { 
     result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION); 
    } catch (SendIntentException e) { 
     Log.e(TAG, "Exception while starting resolution activity", e); 
    } 
} 

@Override 
public void onConnected(Bundle connectionHint) { 
    Log.i(TAG, "API client connected."); 
    if (mBitmapToSave == null) { 
     // This activity has no UI of its own. Just start the camera. 
     startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE), 
       REQUEST_CODE_CAPTURE_IMAGE); 
     return; 
    } 
    saveFileToDrive(); 
} 

@Override 
public void onConnectionSuspended(int cause) { 
    Log.i(TAG, "GoogleApiClient connection suspended"); 
} 

}

+0

मेरे पास यहां एक ही समस्या है। अजीब चीज यह है कि यह पहले से ही काम करने के लिए प्रयोग किया जाता है। मैं अपना ऐप 4.1.2 (एपीआई 16) डिवाइस पर चला रहा हूं। – m02ph3u5

उत्तर

3

यह एक कठिन एक है के बाद से मैं समय पूरी तरह से करने के लिए फिर से रन की जरूरत नहीं है और अपने कोड का विश्लेषण। और इसे चलाने के बिना, मुझे कुछ भी स्पष्ट नहीं दिख रहा है।

लेकिन, चूंकि मेरे पास यह सामान है और मेरे ऐप में चल रहा है, इसलिए मैं मदद करना चाहता हूं। दुर्भाग्य से Google Play सेवाएं कनेक्शन और प्रमाणीकरण कोड मेरे ऐप के टुकड़ों और गतिविधियों में बिखरा हुआ है। इसलिए, मैंने एक डमी गतिविधि बनाने और इसमें सभी सामान खींचने का प्रयास किया। 'सभी सामान' से मेरा मतलब खाता प्रबंधक रैपर (जीए) और संबंधित खाता पिकर कोड है।

परिणाम कुछ 300 लाइनों की गड़बड़ी है जो काम कर सकती हैं, लेकिन मैं कोई दावा नहीं करता हूं। एक नज़र और शुभकामनाएं लें।

package com.......; 

import android.accounts.Account; 
import android.accounts.AccountManager; 
import android.app.Activity; 
import android.app.Dialog; 
import android.app.DialogFragment; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.content.IntentSender; 
import android.content.SharedPreferences; 
import android.os.Bundle; 
import android.preference.PreferenceManager; 
import android.util.Log; 
import android.widget.Toast; 

import com.google.android.gms.auth.GoogleAuthUtil; 
import com.google.android.gms.common.AccountPicker; 
import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.GooglePlayServicesUtil; 
import com.google.android.gms.common.api.GoogleApiClient; 

public class GooApiClient extends Activity implements 
       GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { 

    private static final String DIALOG_ERROR = "dialog_error"; 
    private static final String REQUEST_CODE = "request_code"; 

    private static final int REQ_ACCPICK = 1; 
    private static final int REQ_AUTH = 2; 
    private static final int REQ_RECOVER = 3; 

    private GoogleApiClient mGooApiClient; 
    private boolean mIsInAuth; //block re-entrancy 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    if (checkPlayServices() && checkUserAccount()) { 
     gooInit(); 
     gooConnect(true); 
    } 
    } 

    @Override 
    public void onConnected(Bundle bundle) { 
    Log.d("_", "connected"); 
    } 
    @Override 
    public void onConnectionSuspended(int i) { } 
    @Override 
    public void onConnectionFailed(ConnectionResult result) { 
    Log.d("_", "failed " + result.hasResolution()); 
    if (!mIsInAuth) { 
     if (result.hasResolution()) { 
     try { 
      mIsInAuth = true; 
      result.startResolutionForResult(this, REQ_AUTH); 
     } catch (IntentSender.SendIntentException e) { 
      suicide("authorization fail"); 
     } 
     } else { 
     suicide("authorization fail"); 
     } 
    } 
    } 

    @Override 
    protected void onActivityResult(int requestCode, int resultCode, Intent it) { 
    Log.d("_", "activity result " + requestCode + " " + resultCode); 

    switch (requestCode) { 
     case REQ_AUTH: case REQ_RECOVER: { 
     mIsInAuth = false; 
     if (resultCode == Activity.RESULT_OK) { 
      gooConnect(true); 
     } else if (resultCode == RESULT_CANCELED) { 
      suicide("authorization fail"); 
     } 
     return; 
     } 

     case REQ_ACCPICK: { // return from account picker 
     if (resultCode == Activity.RESULT_OK && it != null) { 
      String emil = it.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); 
      if (GA.setEmil(this, emil) == GA.CHANGED) { 
      gooInit(); 
      gooConnect(true); 
      } 
     } else if (GA.getActiveEmil(this) == null) { 
      suicide("selection failed"); 
     } 
     return; 
     } 
    } 
    super.onActivityResult(requestCode, resultCode, it); // DO NOT REMOVE 
    } 

    private boolean checkPlayServices() { 
    Log.d("_", "check PS"); 
    int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); 
    if (status != ConnectionResult.SUCCESS) { 
     if (GooglePlayServicesUtil.isUserRecoverableError(status)) { 
     mIsInAuth = true; 
     errorDialog(status, LstActivity.REQ_RECOVER); 
     } else { 
     suicide("play services failed"); 
     } 
     return false; 
    } 
    return true; 
    } 
    private boolean checkUserAccount() { 
    String emil = GA.getActiveEmil(this); 
    Account accnt = GA.getPrimaryAccnt(this, true); 
    Log.d("_", "check user account " + emil + " " + accnt); 

    if (emil == null) { // no emil (after install) 
     if (accnt == null) { // multiple or no accounts available, go pick one 
     accnt = GA.getPrimaryAccnt(this, false); 
     Intent it = AccountPicker.newChooseAccountIntent(accnt, null, 
     new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, true, null, null, null, null 
     ); 
     this.startActivityForResult(it, LstActivity.REQ_ACCPICK); 
     return false; //--------------------->>> 

     } else { // there's only one goo account registered with the device, skip the picker 
     GA.setEmil(this, accnt.name); 
     } 

    // UNLIKELY BUT POSSIBLE, emil's OK, but the account have been removed since (through settings) 
    } else { 
     accnt = GA.getActiveAccnt(this); 
     if (accnt == null) { 
     accnt = GA.getPrimaryAccnt(this, false); 
     Intent it = AccountPicker.newChooseAccountIntent(accnt, null, 
     new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, true, null, null, null, null 
     ); 
     this.startActivityForResult(it, LstActivity.REQ_ACCPICK); 
     return false; //------------------>>> 
     } 
    } 
    return true; 
    } 

    private void gooInit(){ 
    String emil = GA.getActiveEmil(this); 
    Log.d("_", "goo init " + emil); 
    if (emil != null){ 
     mGooApiClient = new GoogleApiClient.Builder(this) 
     .setAccountName(emil).addApi(com.google.android.gms.drive.Drive.API) 
     .addScope(com.google.android.gms.drive.Drive.SCOPE_FILE) 
     .addConnectionCallbacks(this).addOnConnectionFailedListener(this) 
     .build(); 
    } 
    } 

    private void gooConnect(boolean bConnect) { 
    Log.d("_", "goo connect " + bConnect); 
    if (mGooApiClient != null) { 
     if (!bConnect) { 
     mGooApiClient.disconnect(); 
     } else if (! (mGooApiClient.isConnecting() || mGooApiClient.isConnected())){ 
     mGooApiClient.connect(); 
     } 
    } 
    } 

    private void suicide(String msg) { 
    GA.removeActiveAccnt(this); 
    Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); 
    finish(); 
    } 

    private void errorDialog(int errorCode, int requestCode) { 
    Bundle args = new Bundle(); 
    args.putInt(DIALOG_ERROR, errorCode); 
    args.putInt(REQUEST_CODE, requestCode); 
    ErrorDialogFragment dialogFragment = new ErrorDialogFragment(); 
    dialogFragment.setArguments(args); 
    dialogFragment.show(getFragmentManager(), "errordialog"); 
    } 
    public static class ErrorDialogFragment extends DialogFragment { 
    public ErrorDialogFragment() { } 
    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     int errorCode = getArguments().getInt(DIALOG_ERROR); 
     int requestCode = getArguments().getInt(DIALOG_ERROR); 
     return GooglePlayServicesUtil.getErrorDialog(errorCode, getActivity(), requestCode); 
    } 
    @Override 
    public void onDismiss(DialogInterface dialog) { 
     getActivity().finish(); 
    } 
    } 

    private static class GA { 
    private static final String ACC_NAME = "account_name"; 
    public static final int FAIL = -1; 
    public static final int UNCHANGED = 0; 
    public static final int CHANGED = +1; 

    private static String mCurrEmil = null; // cache locally 
    private static String mPrevEmil = null; // cache locally 

    public static Account[] getAllAccnts(Context ctx) { 
     return AccountManager.get(acx(ctx)).getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE); 
    } 

    public static Account getPrimaryAccnt(Context ctx, boolean bOneOnly) { 
     Account[] accts = getAllAccnts(ctx); 
     if (bOneOnly) 
     return accts == null || accts.length != 1 ? null : accts[0]; 
     return accts == null || accts.length == 0 ? null : accts[0]; 
    } 

    public static Account getActiveAccnt(Context ctx) { 
     return emil2Accnt(ctx, getActiveEmil(ctx)); 
    } 

    public static String getActiveEmil(Context ctx) { 
     if (mCurrEmil != null) { 
     return mCurrEmil; 
     } 
     mCurrEmil = ctx == null ? null : pfs(ctx).getString(ACC_NAME, null); 
     return mCurrEmil; 
    } 

    public static Account getPrevEmil(Context ctx) { 
     return emil2Accnt(ctx, mPrevEmil); 
    } 

    public static Account emil2Accnt(Context ctx, String emil) { 
     if (emil != null) { 
     Account[] accounts = 
     AccountManager.get(acx(ctx)).getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE); 
     for (Account account : accounts) { 
      if (emil.equalsIgnoreCase(account.name)) { 
      return account; 
      } 
     } 
     } 
     return null; 
    } 

    /** 
    * Stores a new email in persistent app storage, reporting result 
    * @param newEmil new email, optionally null 
    * @param ctx activity context 
    * @return FAIL, CHANGED or UNCHANGED (based on the following table) 
    * OLD NEW SAVED RESULT 
    * ERROR    FAIL 
    * null null null FAIL 
    * null new new  CHANGED 
    * old null old  UNCHANGED 
    * old != new new  CHANGED 
    * old == new new  UNCHANGED 
    */ 
    public static int setEmil(Context ctx, String newEmil) { 
     int result = FAIL; // 0 0 

     mPrevEmil = getActiveEmil(ctx); 
     if  ((mPrevEmil == null) && (newEmil != null)) { 
     result = CHANGED; 
     } else if ((mPrevEmil != null) && (newEmil == null)) { 
     result = UNCHANGED; 
     } else if ((mPrevEmil != null) && (newEmil != null)) { 
     result = mPrevEmil.equalsIgnoreCase(newEmil) ? UNCHANGED : CHANGED; 
     } 
     if (result == CHANGED) { 
     mCurrEmil = newEmil; 
     pfs(ctx).edit().putString(ACC_NAME, newEmil).apply(); 
     } 
     return result; 
    } 
    public static void removeActiveAccnt(Context ctx) { 
     mCurrEmil = null; 
     pfs(ctx).edit().remove(ACC_NAME).apply(); 
    } 

    private static Context acx(Context ctx) { 
     return ctx == null ? null : ctx.getApplicationContext(); 
    } 
    private static SharedPreferences pfs(Context ctx) { 
     return ctx == null ? null : PreferenceManager.getDefaultSharedPreferences(acx(ctx)); 
    } 
    } 
} 

Btw, मुझे पता है कि 'ईमेल' वर्तनी, 'एमिल सिर्फ मेरे चाचा के नाम हुआ और मैं विरोध नहीं कर सकता है :-)

अद्यतन (2015-अप्रैल 11) :

मैंने हाल ही में Google ड्राइव प्राधिकरण और खाता स्विचिंग को संभालने वाले कोड का फिर से दौरा किया है। नतीजा here पाया जा सकता है और यह आरईएसटी और जीडीएए एपीआई दोनों का समर्थन करता है।

+0

** बूलियन mIsInAuth ** मेरे लिए चाल है - आश्चर्य है कि यह Google dev docs में क्यों नहीं है ... – m02ph3u5

+0

@ m02ph3u5 - यह * उनके दस्तावेज़ों में है, इसे __mIntentInProgress__ कहा जाता है (देखें: https: // डेवलपर .google.com/+/मोबाइल/एंड्रॉइड/साइन-इन) –

+0

@ देव-आईएल नहीं मिला * mIntentInProgess * प्लस यह जी + डॉक्स में है - यह GoogleApiClient दस्तावेज़ों में क्यों नहीं है? – m02ph3u5

3

ऐसा इसलिए होता है क्योंकि पहले लॉगिन/प्राधिकरण के बाद एंड्रॉइड एक ही डिफ़ॉल्ट खाता पैरामीटर का उपयोग करता रहता है। यदि आप लूप से बचना चाहते हैं और सुनिश्चित करें कि पिकर फिर से दिखाता है तो आपको फिर से कनेक्ट करने से पहले Plus.AccountApi.clearDefaultAccount (mGoogleApiClient) को कॉल करके पूरी तरह से डिफ़ॉल्ट खाता साफ़ करना होगा।

इसे प्राप्त करने के लिए, आपको प्लस जोड़ना होगा।(समस्याओं जब खाते को बदलने API क्लाइंट के पुनर्निर्माण से बचा जाता है)

 mGoogleApiClient = new GoogleApiClient.Builder(this) 
      .addApi(Drive.API) 
      .addApi(Plus.API) 
      .addScope(Drive.SCOPE_FILE) 
      .addConnectionCallbacks(this) 
      .addOnConnectionFailedListener(this) 
      .build(); 

और फिर आप API क्लाइंट का पुनर्निर्माण और किसी दूसरे खाते में जोड़ने से पहले डिफ़ॉल्ट खाता साफ कर सकते हैं: GoogleApiClient बिल्डर के लिए API क्षेत्र के

// if the api client existed, we terminate it 
    if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) { 
     Plus.AccountApi.clearDefaultAccount(mGoogleApiClient); 
     mGoogleApiClient.disconnect(); 
    } 
    // build new api client to avoid problems reusing it 
    mGoogleApiClient = new GoogleApiClient.Builder(this) 
      .addApi(Drive.API) 
      .addApi(Plus.API) 
      .addScope(Drive.SCOPE_FILE) 
      .setAccountName(account) 
      .addConnectionCallbacks(this) 
      .addOnConnectionFailedListener(this) 
      .build(); 
    mGoogleApiClient.connect(); 

इस तरह प्लस.एपीआई स्कोप का उपयोग करने के लिए कोई अतिरिक्त अनुमति या एपीआई सक्रियण की आवश्यकता नहीं है। मुझे आशा है कि इससे आपकी समस्या में मदद मिलेगी।

+0

मेरे उदाहरण के साथ, यह काम नहीं किया। क्या आप Google के साथ लॉगिन करने के लिए एक अच्छा उदाहरण सुझा सकते हैं? ........ मैंने यह कोशिश की जहां मेरे पास एडीएपीआई के लिए यह लाइन है: .addApi (Plus.API, Plus.PlusOptions.builder()। Build()) .. और निम्न त्रुटि मिली: विफलता परिणाम परिणाम प्रदान करना {who = null, request = 0, परिणाम = 0, डेटा = null} गतिविधि {com ... androidgoogleplusloginexample.AndroidGooglePlusExample}: java.lang.IllegalStateException: GoogleApiClient को कनेक्ट होना चाहिए। ..... धन्यवाद – gnB

+0

मैंने कनेक्शन उदाहरण को अधिक विस्तृत और अद्यतित करने के लिए अपना उत्तर संपादित कर लिया है। जैसा कि आप देख सकते हैं, अगर मैं एपीआई क्लाइंट पहले से ही किसी खाते से जुड़ा हुआ हूं तो मैं केवल डिफ़ॉल्ट खाता साफ़ कर दूंगा। यदि ऐसा नहीं है, तो आपको वर्णित त्रुटि मिल जाएगी। मुझे लगता है कि आप पहले कनेक्शन से पहले डिफ़ॉल्ट खाते को साफ़ करने का प्रयास कर रहे हैं, और यही कारण है कि आपको त्रुटि मिलती है। इसे किसी अन्य खाते से फिर से कनेक्ट करते समय करें। – jmart

संबंधित मुद्दे